This commit is contained in:
Dheeraj Bansal 2026-07-04 14:38:16 +00:00 committed by GitHub
commit 7f97c57447
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 146 additions and 70 deletions

View File

@ -38,8 +38,13 @@ under the License.
<!-- ============================== -->
<Console name="CONSOLE" target="SYSTEM_OUT">
<ThresholdFilter level="OFF" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="%-5p [%c{3}] (%t:%x) (logid:%X{logcontextid}) %m%ex%n"/>
<ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="ACCEPT"/>
<PatternLayout pattern="%d{DEFAULT} %-5p [%c{3}] (%t:%x) (logid:%X{logcontextid}) %m%ex%n"/>
</Console>
<Console name="CONSOLE_ERR" target="SYSTEM_ERR">
<ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="%d{DEFAULT} %-5p [%c{3}] (%t:%x) (logid:%X{logcontextid}) %m%ex%n"/>
</Console>
</Appenders>
@ -50,17 +55,11 @@ under the License.
<!-- ================ -->
<Logger name="com.cloud" level="INFO"/>
<Logger name="org.apache" level="INFO"/>
<Logger name="org" level="INFO"/>
<Logger name="net" level="INFO"/>
<Logger name="com.amazonaws" level="INFO"/>
<Logger name="httpclient.wire" level="INFO"/>
<Logger name="org.apache.http.wire" level="INFO"/>
<!-- ======================= -->
@ -69,6 +68,7 @@ under the License.
<Root level="INFO">
<AppenderRef ref="CONSOLE"/>
<AppenderRef ref="CONSOLE_ERR"/>
<AppenderRef ref="FILE"/>
</Root>

View File

@ -21,7 +21,7 @@ under the License.
<Appenders>
<properties>
<property name="filters">net.sf.cglib.proxy</property>
<property name="filters">net.sf.cglib.proxy</property>
</properties>
<!-- ================================= -->
@ -37,7 +37,6 @@ under the License.
<PatternLayout pattern="%d{DEFAULT} %-5p [%c{1.}] (%t:%x) (logid:%X{logcontextid}) %m%ex{filters(${filters})}%n"/>
</RollingFile>
<RollingFile name="APISERVER" append="true" fileName="@APISERVERLOG@" filePattern="@APISERVERLOG@.%d{yyyy-MM-dd}.gz">
<ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
<Policies>
@ -69,8 +68,13 @@ under the License.
<!-- ============================== -->
<Console name="CONSOLE" target="SYSTEM_OUT">
<ThresholdFilter level="OFF" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="%-5p [%c{1.}] (%t:%x) (logid:%X{logcontextid}) %m%ex{filters(${filters})}%n"/>
<ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="ACCEPT"/>
<PatternLayout pattern="%d{DEFAULT} %-5p [%c{1.}] (%t:%x) (logid:%X{logcontextid}) %m%ex{filters(${filters})}%n"/>
</Console>
<Console name="CONSOLE_ERR" target="SYSTEM_ERR">
<ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="%d{DEFAULT} %-5p [%c{1.}] (%t:%x) (logid:%X{logcontextid}) %m%ex{filters(${filters})}%n"/>
</Console>
<!-- ============================== -->
@ -78,7 +82,7 @@ under the License.
<!-- ============================== -->
<SnmpTrapAppender name="SNMP" SnmpManagerIpAddresses="" SnmpManagerPorts="" SnmpManagerCommunities="">
<ThresholdFilter level="WARN" onMatch="ACCEPT" onMismatch="DENY"/>
<ThresholdFilter level="WARN" onMatch="ACCEPT" onMismatch="DENY"/>
</SnmpTrapAppender>
</Appenders>
@ -102,7 +106,7 @@ under the License.
<Logger name="net" level="INFO"/>
<Logger name="apiserver.com.cloud" level="DEBUG"/>
<Logger name="apiserver.com.cloud" level="DEBUG"/>
<Logger name="apiserver.com.cloud" level="DEBUG" additivity="false">
<AppenderRef ref="APISERVER"/>
@ -115,6 +119,7 @@ under the License.
<Logger name="org.apache.cloudstack.alert" additivity="false" level="WARN">
<AppenderRef ref="SYSLOG"/>
<AppenderRef ref="CONSOLE"/>
<AppenderRef ref="CONSOLE_ERR"/>
<AppenderRef ref="FILE"/>
<AppenderRef ref="SNMP"/>
<AppenderRef ref="ALERTSYSLOG"/>
@ -127,6 +132,7 @@ under the License.
<Root level="INFO">
<AppenderRef ref="SYSLOG"/>
<AppenderRef ref="CONSOLE"/>
<AppenderRef ref="CONSOLE_ERR"/>
<AppenderRef ref="FILE"/>
</Root>

View File

@ -24,7 +24,6 @@ under the License.
<!-- Preserve messages in a local file -->
<!-- ================================= -->
<!-- A regular appender -->
<RollingFile name="FILE" append="true" fileName="@MSLOG@" filePattern="@MSLOG@.%d{yyyy-MM-dd}.gz">
<ThresholdFilter level="TRACE" onMatch="ACCEPT" onMismatch="DENY"/>
@ -34,7 +33,6 @@ under the License.
<PatternLayout pattern="%d{DEFAULT} %-5p [%c{1.}] (%t:%x) %m%ex%n"/>
</RollingFile>
<RollingFile name="INFO-FILE" append="true" fileName="/var/log/cloudstack/management/management-server-info.log" filePattern="/var/log/cloudstack/management/management-server-info.log.%d{yyyy-MM-dd}.gz">
<ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
<Policies>
@ -43,7 +41,6 @@ under the License.
<PatternLayout pattern="%d{DEFAULT} %-5p [%c{1.}] (%t:%x) (logid:%X{logcontextid}) %m%ex%n"/>
</RollingFile>
<RollingFile name="APISERVER" append="true" fileName="@APISERVERLOG@" filePattern="@APISERVERLOG@.%d{yyyy-MM-dd}.gz">
<ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
<Policies>
@ -56,7 +53,6 @@ under the License.
<!-- Append warnings+ to the syslog if it is listening on UDP port -->
<!-- ============================== -->
<Syslog name="SYSLOG" host="localhost" facility="LOCAL6">
<ThresholdFilter level="WARN" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="%-5p [%c{1.}] (%t:%x) %m%ex%n"/>
@ -67,8 +63,13 @@ under the License.
<!-- ============================== -->
<Console name="CONSOLE" target="SYSTEM_OUT">
<ThresholdFilter level="OFF" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="%-5p [%c{1.}] (%t:%x) %m%ex%n"/>
<ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="ACCEPT"/>
<PatternLayout pattern="%d{DEFAULT} %-5p [%c{1.}] (%t:%x) %m%ex%n"/>
</Console>
<Console name="CONSOLE_ERR" target="SYSTEM_ERR">
<ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="%d{DEFAULT} %-5p [%c{1.}] (%t:%x) %m%ex%n"/>
</Console>
</Appenders>
@ -101,6 +102,7 @@ under the License.
<Logger name="org.apache.cloudstack.alerts" additivity="false" level="WARN">
<AppenderRef ref="SYSLOG"/>
<AppenderRef ref="CONSOLE"/>
<AppenderRef ref="CONSOLE_ERR"/>
<AppenderRef ref="FILE"/>
<AppenderRef ref="SNMP"/>
<AppenderRef ref="ALERTSYSLOG"/>
@ -113,6 +115,7 @@ under the License.
<Root level="INFO">
<AppenderRef ref="SYSLOG"/>
<AppenderRef ref="CONSOLE"/>
<AppenderRef ref="CONSOLE_ERR"/>
<AppenderRef ref="FILE"/>
<AppenderRef ref="INFO-FILE"/>
</Root>

View File

@ -80,6 +80,11 @@ public class ConsoleProxy {
static String factoryClzName;
static boolean standaloneStart = false;
/**
* Session timeout in milliseconds, default 300000 (5 minutes).
*/
public static int sessionTimeoutMillis = 300000;
static String encryptorPassword = "Dummy";
static final String[] skipProperties = new String[]{"certificate", "cacertificate", "keystore_password", "privatekey"};
@ -92,11 +97,13 @@ public class ConsoleProxy {
private static void configLog4j() {
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
URL configUrl = loader.getResource("/conf/log4j-cloud.xml");
if (configUrl == null)
if (configUrl == null) {
configUrl = ClassLoader.getSystemResource("log4j-cloud.xml");
}
if (configUrl == null)
if (configUrl == null) {
configUrl = ClassLoader.getSystemResource("conf/log4j-cloud.xml");
}
if (configUrl != null) {
try {
@ -121,9 +128,8 @@ public class ConsoleProxy {
private static void configProxy(Properties conf) {
LOGGER.info("Configure console proxy...");
for (Object key : conf.keySet()) {
LOGGER.info("Property " + (String)key + ": " + conf.getProperty((String)key));
if (!ArrayUtils.contains(skipProperties, key)) {
LOGGER.info("Property " + (String)key + ": " + conf.getProperty((String)key));
LOGGER.info("Property " + (String) key + ": " + conf.getProperty((String) key));
}
}
@ -165,13 +171,31 @@ public class ConsoleProxy {
defaultBufferSize = Integer.parseInt(s);
LOGGER.info("Setting defaultBufferSize=" + defaultBufferSize);
}
// Read consoleproxy.session.timeout in milliseconds.
s = conf.getProperty("consoleproxy.session.timeout");
if (s != null) {
try {
int parsedTimeout = Integer.parseInt(s);
if (parsedTimeout < 1000) {
LOGGER.warn("Invalid value for consoleproxy.session.timeout: " + s
+ " ms, must be >= 1000 ms, keeping default " + sessionTimeoutMillis + " ms");
} else {
sessionTimeoutMillis = parsedTimeout;
LOGGER.info("Setting consoleproxy.session.timeout=" + sessionTimeoutMillis + " ms");
}
} catch (NumberFormatException e) {
LOGGER.warn("Invalid value for consoleproxy.session.timeout: " + s
+ ", keeping default " + sessionTimeoutMillis + " ms", e);
}
}
}
public static ConsoleProxyServerFactory getHttpServerFactory() {
try {
Class<?> clz = Class.forName(factoryClzName);
try {
ConsoleProxyServerFactory factory = (ConsoleProxyServerFactory)clz.newInstance();
ConsoleProxyServerFactory factory = (ConsoleProxyServerFactory) clz.newInstance();
factory.init(ConsoleProxy.ksBits, ConsoleProxy.ksPassword);
return factory;
} catch (InstantiationException e) {
@ -243,7 +267,7 @@ public class ConsoleProxy {
}
if (result != null && result instanceof String) {
authResult = new Gson().fromJson((String)result, ConsoleProxyAuthenticationResult.class);
authResult = new Gson().fromJson((String) result, ConsoleProxyAuthenticationResult.class);
} else {
LOGGER.error("Invalid authentication return object " + result + " for vm: " + param.getClientTag() + ", decline the access");
authResult.setSuccess(false);
@ -318,19 +342,25 @@ public class ConsoleProxy {
LOGGER.error("Unable to setup private channel due to ClassNotFoundException", e);
}
// ensure we have a Properties object before merging defaults
if (conf == null) {
conf = new Properties();
}
// merge properties from conf file
InputStream confs = ConsoleProxy.class.getResourceAsStream("/conf/consoleproxy.properties");
Properties props = new Properties();
if (confs == null) {
final File file = PropertiesUtil.findConfigFile("consoleproxy.properties");
if (file == null)
if (file == null) {
LOGGER.info("Can't load consoleproxy.properties from classpath, will use default configuration");
else
} else {
try {
confs = new FileInputStream(file);
} catch (FileNotFoundException e) {
LOGGER.info("Ignoring file not found exception and using defaults");
}
}
}
if (confs != null) {
try {
@ -339,15 +369,18 @@ public class ConsoleProxy {
for (Object key : props.keySet()) {
// give properties passed via context high priority, treat properties from consoleproxy.properties
// as default values
if (conf.get(key) == null)
if (conf.get(key) == null) {
conf.put(key, props.get(key));
}
}
} catch (Exception e) {
LOGGER.error(e.toString(), e);
}
}
try {
confs.close();
if (confs != null) {
confs.close();
}
} catch (IOException e) {
LOGGER.error("Failed to close consolepropxy.properties : " + e.toString(), e);
}
@ -481,8 +514,9 @@ public class ConsoleProxy {
ConsoleProxyClientStatsCollector statsCollector = getStatsCollector();
String loadInfo = statsCollector.getStatsReport();
reportLoadInfo(loadInfo);
if (LOGGER.isDebugEnabled())
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Report load change : " + loadInfo);
}
}
return viewer;
@ -506,13 +540,15 @@ public class ConsoleProxy {
// protected against malicious attack by modifying URL content
if (ajaxSession != null) {
long ajaxSessionIdFromUrl = Long.parseLong(ajaxSession);
if (ajaxSessionIdFromUrl != viewer.getAjaxSessionId())
if (ajaxSessionIdFromUrl != viewer.getAjaxSessionId()) {
throw new AuthenticationException("Cannot use the existing viewer " + viewer + ": modified AJAX session id");
}
}
if (param.getClientHostPassword() == null || param.getClientHostPassword().isEmpty() ||
!param.getClientHostPassword().equals(viewer.getClientHostPassword()))
!param.getClientHostPassword().equals(viewer.getClientHostPassword())) {
throw new AuthenticationException("Cannot use the existing viewer " + viewer + ": bad sid");
}
if (!viewer.isFrontEndAlive()) {
@ -526,8 +562,9 @@ public class ConsoleProxy {
ConsoleProxyClientStatsCollector statsCollector = getStatsCollector();
String loadInfo = statsCollector.getStatsReport();
reportLoadInfo(loadInfo);
if (LOGGER.isDebugEnabled())
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Report load change : " + loadInfo);
}
}
return viewer;
}
@ -593,7 +630,7 @@ public class ConsoleProxy {
}
public static ConsoleProxyNoVncClient getNoVncViewer(ConsoleProxyClientParam param, String ajaxSession,
Session session) throws AuthenticationException {
Session session) throws AuthenticationException {
boolean reportLoadChange = false;
String clientKey = param.getClientMapKey();
LOGGER.debug("Getting NoVNC viewer for {}. Session requires new viewer: {}, client tag: {}. session UUID: {}",
@ -609,8 +646,9 @@ public class ConsoleProxy {
reportLoadChange = true;
} else {
if (param.getClientHostPassword() == null || param.getClientHostPassword().isEmpty() ||
!param.getClientHostPassword().equals(viewer.getClientHostPassword()))
!param.getClientHostPassword().equals(viewer.getClientHostPassword())) {
throw new AuthenticationException("Cannot use the existing viewer " + viewer + ": bad sid");
}
try {
authenticationExternally(param);
@ -620,7 +658,7 @@ public class ConsoleProxy {
}
LOGGER.info("Initializing new novnc client and disconnecting existing session");
try {
((ConsoleProxyNoVncClient)viewer).getSession().disconnect();
((ConsoleProxyNoVncClient) viewer).getSession().disconnect();
} catch (IOException e) {
LOGGER.error("Exception while disconnect session of novnc viewer object: " + viewer, e);
}
@ -635,10 +673,11 @@ public class ConsoleProxy {
ConsoleProxyClientStatsCollector statsCollector = getStatsCollector();
String loadInfo = statsCollector.getStatsReport();
reportLoadInfo(loadInfo);
if (LOGGER.isDebugEnabled())
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Report load change : " + loadInfo);
}
}
return (ConsoleProxyNoVncClient)viewer;
return (ConsoleProxyNoVncClient) viewer;
}
}
}

View File

@ -22,8 +22,8 @@ import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
*
@ -34,7 +34,7 @@ import org.apache.logging.log4j.LogManager;
public class ConsoleProxyGCThread extends Thread {
protected Logger logger = LogManager.getLogger(ConsoleProxyGCThread.class);
private final static int MAX_SESSION_IDLE_SECONDS = 180;
private static final int DEFAULT_MAX_SESSION_IDLE_SECONDS = 180;
private final Map<String, ConsoleProxyClient> connMap;
private final Set<String> removedSessionsSet;
@ -45,22 +45,30 @@ public class ConsoleProxyGCThread extends Thread {
this.removedSessionsSet = removedSet;
}
private int getMaxSessionIdleSeconds() {
if (ConsoleProxy.sessionTimeoutMillis <= 0) {
return DEFAULT_MAX_SESSION_IDLE_SECONDS;
}
return Math.max(1, ConsoleProxy.sessionTimeoutMillis / 1000);
}
private void cleanupLogging() {
if (lastLogScan != 0 && System.currentTimeMillis() - lastLogScan < 3600000)
if (lastLogScan != 0 && System.currentTimeMillis() - lastLogScan < 3600000) {
return;
}
lastLogScan = System.currentTimeMillis();
File logDir = new File("./logs");
File files[] = logDir.listFiles();
File[] files = logDir.listFiles();
if (files != null) {
for (File file : files) {
if (System.currentTimeMillis() - file.lastModified() >= 86400000L) {
try {
file.delete();
} catch (Throwable e) {
logger.info("[ignored]"
+ "failed to delete file: " + e.getLocalizedMessage());
logger.info("[ignored]failed to delete file: " + e.getLocalizedMessage());
}
}
}
@ -69,7 +77,6 @@ public class ConsoleProxyGCThread extends Thread {
@Override
public void run() {
boolean bReportLoad = false;
long lastReportTick = System.currentTimeMillis();
@ -80,6 +87,7 @@ public class ConsoleProxyGCThread extends Thread {
if (logger.isDebugEnabled()) {
logger.debug(String.format("connMap=%s, removedSessions=%s", connMap, removedSessionsSet));
}
Set<String> e = connMap.keySet();
Iterator<String> iterator = e.iterator();
while (iterator.hasNext()) {
@ -91,8 +99,8 @@ public class ConsoleProxyGCThread extends Thread {
client = connMap.get(key);
}
long seconds_unused = (System.currentTimeMillis() - client.getClientLastFrontEndActivityTime()) / 1000;
if (seconds_unused < MAX_SESSION_IDLE_SECONDS) {
long secondsUnused = (System.currentTimeMillis() - client.getClientLastFrontEndActivityTime()) / 1000;
if (secondsUnused < getMaxSessionIdleSeconds()) {
continue;
}
@ -101,18 +109,17 @@ public class ConsoleProxyGCThread extends Thread {
bReportLoad = true;
}
// close the server connection
logger.info("Dropping " + client + " which has not been used for " + seconds_unused + " seconds");
logger.info("Dropping " + client + " which has not been used for " + secondsUnused + " seconds");
client.closeClient();
}
if (bReportLoad || System.currentTimeMillis() - lastReportTick > 5000) {
// report load changes
ConsoleProxyClientStatsCollector collector = new ConsoleProxyClientStatsCollector(connMap);
collector.setRemovedSessions(new ArrayList<>(removedSessionsSet));
String loadInfo = collector.getStatsReport();
ConsoleProxy.reportLoadInfo(loadInfo);
lastReportTick = System.currentTimeMillis();
synchronized (removedSessionsSet) {
removedSessionsSet.clear();
}

View File

@ -95,14 +95,16 @@ public class ConsoleProxyNoVNCHandler extends WebSocketHandler {
String clientIp = session.getRemoteAddress().getAddress().getHostAddress();
boolean sessionRequiresNewViewer = Boolean.parseBoolean(queryMap.get("sessionRequiresNewViewer"));
if (tag == null)
if (tag == null) {
tag = "";
}
long ajaxSessionId = 0;
int port;
if (host == null || portStr == null || sid == null)
if (host == null || portStr == null || sid == null) {
throw new IllegalArgumentException();
}
try {
port = Integer.parseInt(portStr);
@ -125,6 +127,14 @@ public class ConsoleProxyNoVNCHandler extends WebSocketHandler {
}
try {
if (ConsoleProxy.sessionTimeoutMillis > 0) {
session.setIdleTimeout(ConsoleProxy.sessionTimeoutMillis);
logger.debug("Set noVNC WebSocket idle timeout to {} ms for session UUID {}.",
ConsoleProxy.sessionTimeoutMillis, sessionUuid);
} else {
logger.debug("Using default noVNC WebSocket idle timeout for session UUID {}.", sessionUuid);
}
ConsoleProxyClientParam param = new ConsoleProxyClientParam();
param.setClientHostAddress(host);
param.setClientHostPort(port);
@ -185,12 +195,21 @@ public class ConsoleProxyNoVNCHandler extends WebSocketHandler {
@OnWebSocketFrame
public void onFrame(Frame f) throws IOException {
if (viewer == null) {
logger.warn("Ignoring WebSocket frame because viewer is not initialized yet.");
return;
}
logger.trace("Sending client [ID: {}] frame of {} bytes.", viewer.getClientId(), f.getPayloadLength());
viewer.sendClientFrame(f);
}
@OnWebSocketError
public void onError(Throwable cause) {
logger.error("Error on WebSocket [client ID: {}, session UUID: {}].", cause, viewer.getClientId(), viewer.getSessionUuid());
if (viewer != null) {
logger.error("Error on WebSocket [client ID: {}, session UUID: {}].",
viewer.getClientId(), viewer.getSessionUuid(), cause);
} else {
logger.error("Error on WebSocket before viewer initialization.", cause);
}
}
}

View File

@ -20,19 +20,23 @@ under the License.
<Configuration monitorInterval="60">
<Appenders>
<!-- ============================== -->
<!-- Append messages to the console -->
<!-- ============================== -->
<!-- ============================== -->
<!-- Append messages to the console -->
<!-- ============================== -->
<Console name="CONSOLE" target="SYSTEM_OUT">
<ThresholdFilter level="OFF" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="%d{ABSOLUTE}{GMT} %5p %c{1}:%L - %m%ex%n"/>
</Console>
<Console name="CONSOLE" target="SYSTEM_OUT">
<ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="ACCEPT"/>
<PatternLayout pattern="%d{ABSOLUTE}{GMT} %5p %c{1}:%L - %m%ex%n"/>
</Console>
<Console name="CONSOLE_ERR" target="SYSTEM_ERR">
<ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="%d{ABSOLUTE}{GMT} %5p %c{1}:%L - %m%ex%n"/>
</Console>
<!-- ================================ -->
<!-- Append messages to the usage log -->
<!-- ================================ -->
<!-- ================================ -->
<!-- Append messages to the usage log -->
<!-- ================================ -->
<RollingFile name="USAGE" append="true" fileName="@USAGELOG@" filePattern="@USAGELOG@.%d{yyyy-MM-dd}{GMT}.gz">
<ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
@ -44,16 +48,13 @@ under the License.
</Appenders>
<Loggers>
<!-- ================ -->
<!-- Limit categories -->
<!-- ================ -->
<!-- ================ -->
<!-- Limit categories -->
<!-- ================ -->
<Logger name="com.cloud" level="DEBUG"/>
<Logger name="org.apache" level="INFO"/>
<Logger name="org" level="INFO"/>
<Logger name="net" level="INFO"/>
<!-- ======================= -->
@ -62,6 +63,7 @@ under the License.
<Root level="INFO">
<AppenderRef ref="CONSOLE"/>
<AppenderRef ref="CONSOLE_ERR"/>
<AppenderRef ref="USAGE"/>
</Root>