This is an automated email from the ASF dual-hosted git repository.

kiranchavala pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/main by this push:
     new cddd2c6e57b Add timestamps to management, server and agent .err logs 
(#12967)
cddd2c6e57b is described below

commit cddd2c6e57b85396b9836cc222f34fa285218b5a
Author: Dheeraj Bansal <[email protected]>
AuthorDate: Wed Sep 9 17:06:12 2026 +0530

    Add timestamps to management, server and agent .err logs (#12967)
    
    * Add timestamps to management, server and agent .err logs
    
    * Add timestamps to stderr console logs for agent, management and API server
    
    * usage: add timestamped stderr console logging
    
    * WIP: console proxy timeout changes
    
    * Clean up whitespace in log4j config files
---
 agent/conf/log4j-cloud.xml.in                      | 16 ++---
 client/conf/log4j-cloud.xml.in                     | 18 +++--
 server/conf/log4j-cloud.xml.in                     | 15 +++--
 .../java/com/cloud/consoleproxy/ConsoleProxy.java  | 77 ++++++++++++++++------
 .../cloud/consoleproxy/ConsoleProxyGCThread.java   | 31 +++++----
 .../consoleproxy/ConsoleProxyNoVNCHandler.java     | 25 ++++++-
 usage/conf/log4j-cloud_usage.xml.in                | 34 +++++-----
 7 files changed, 146 insertions(+), 70 deletions(-)

diff --git a/agent/conf/log4j-cloud.xml.in b/agent/conf/log4j-cloud.xml.in
index 84957edca03..87cc9ae5424 100644
--- a/agent/conf/log4j-cloud.xml.in
+++ b/agent/conf/log4j-cloud.xml.in
@@ -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>
 
diff --git a/client/conf/log4j-cloud.xml.in b/client/conf/log4j-cloud.xml.in
index 26da171269d..a1e73a412c3 100755
--- a/client/conf/log4j-cloud.xml.in
+++ b/client/conf/log4j-cloud.xml.in
@@ -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>
 
diff --git a/server/conf/log4j-cloud.xml.in b/server/conf/log4j-cloud.xml.in
index 9a8e5dc7bf3..06fa7527a52 100755
--- a/server/conf/log4j-cloud.xml.in
+++ b/server/conf/log4j-cloud.xml.in
@@ -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>
diff --git 
a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxy.java
 
b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxy.java
index e75ccea9044..b0a2a1fca76 100644
--- 
a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxy.java
+++ 
b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxy.java
@@ -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;
         }
     }
 }
diff --git 
a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyGCThread.java
 
b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyGCThread.java
index 0e8f576cf6d..66d14e4da79 100644
--- 
a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyGCThread.java
+++ 
b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyGCThread.java
@@ -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();
                 }
diff --git 
a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyNoVNCHandler.java
 
b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyNoVNCHandler.java
index a148b988e40..0f8b54e078e 100644
--- 
a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyNoVNCHandler.java
+++ 
b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyNoVNCHandler.java
@@ -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);
+        }
     }
 }
diff --git a/usage/conf/log4j-cloud_usage.xml.in 
b/usage/conf/log4j-cloud_usage.xml.in
index 871d6fb5a7a..fb09b4374ac 100644
--- a/usage/conf/log4j-cloud_usage.xml.in
+++ b/usage/conf/log4j-cloud_usage.xml.in
@@ -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>
 

Reply via email to