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

sureshanaparti 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 9712b4d322a custom AccessLogger (#9733)
9712b4d322a is described below

commit 9712b4d322a685193d36d84c19c281178a1e7300
Author: dahn <[email protected]>
AuthorDate: Mon Aug 4 13:06:08 2025 +0200

    custom AccessLogger (#9733)
    
    * custom AccessLogger
    
    Co-authored-by: Daan Hoogland <[email protected]>
---
 .../java/org/apache/cloudstack/ACSRequestLog.java  | 84 ++++++++++++++++++++++
 .../java/org/apache/cloudstack/ServerDaemon.java   |  3 +-
 .../src/main/java/com/cloud/utils/StringUtils.java | 81 ++++++++-------------
 .../test/java/com/cloud/utils/StringUtilsTest.java | 15 ++++
 4 files changed, 129 insertions(+), 54 deletions(-)

diff --git a/client/src/main/java/org/apache/cloudstack/ACSRequestLog.java 
b/client/src/main/java/org/apache/cloudstack/ACSRequestLog.java
new file mode 100644
index 00000000000..123d2761e00
--- /dev/null
+++ b/client/src/main/java/org/apache/cloudstack/ACSRequestLog.java
@@ -0,0 +1,84 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+package org.apache.cloudstack;
+
+import com.cloud.utils.StringUtils;
+import org.eclipse.jetty.server.NCSARequestLog;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.util.DateCache;
+import org.eclipse.jetty.util.component.LifeCycle;
+
+import java.util.Locale;
+import java.util.TimeZone;
+
+import static 
org.apache.commons.configuration.DataConfiguration.DEFAULT_DATE_FORMAT;
+
+public class ACSRequestLog extends NCSARequestLog {
+    private static final ThreadLocal<StringBuilder> buffers =
+            ThreadLocal.withInitial(() -> new StringBuilder(256));
+
+    private final DateCache dateCache;
+
+    public ACSRequestLog() {
+        super();
+
+        TimeZone timeZone = TimeZone.getTimeZone("GMT");
+        Locale locale = Locale.getDefault();
+        dateCache = new DateCache(DEFAULT_DATE_FORMAT, locale, timeZone);
+    }
+
+    @Override
+    public void log(Request request, Response response) {
+        String requestURI = StringUtils.cleanString(request.getOriginalURI());
+        try {
+            StringBuilder sb = buffers.get();
+            sb.setLength(0);
+
+            sb.append(request.getHttpChannel().getEndPoint()
+                            .getRemoteAddress().getAddress()
+                            .getHostAddress())
+                    .append(" - - [")
+                    .append(dateCache.format(request.getTimeStamp()))
+                    .append("] \"")
+                    .append(request.getMethod())
+                    .append(" ")
+                    .append(requestURI)
+                    .append(" ")
+                    .append(request.getProtocol())
+                    .append("\" ")
+                    .append(response.getStatus())
+                    .append(" ")
+                    .append(response.getHttpChannel().getBytesWritten()) // 
apply filter here?
+                    .append(" \"-\" \"")
+                    .append(request.getHeader("User-Agent"))
+                    .append("\"");
+
+            write(sb.toString());
+        } catch (Exception e) {
+            LOG.warn("Unable to log request", e);
+        }
+    }
+
+    @Override
+    protected void stop(LifeCycle lifeCycle) throws Exception {
+        buffers.remove();
+        super.stop(lifeCycle);
+    }
+}
diff --git a/client/src/main/java/org/apache/cloudstack/ServerDaemon.java 
b/client/src/main/java/org/apache/cloudstack/ServerDaemon.java
index c6fd2ff24dc..259a99330df 100644
--- a/client/src/main/java/org/apache/cloudstack/ServerDaemon.java
+++ b/client/src/main/java/org/apache/cloudstack/ServerDaemon.java
@@ -32,7 +32,6 @@ import org.apache.commons.lang3.StringUtils;
 import org.eclipse.jetty.jmx.MBeanContainer;
 import org.eclipse.jetty.server.HttpConfiguration;
 import org.eclipse.jetty.server.HttpConnectionFactory;
-import org.eclipse.jetty.server.NCSARequestLog;
 import org.eclipse.jetty.server.RequestLog;
 import org.eclipse.jetty.server.SecureRequestCustomizer;
 import org.eclipse.jetty.server.Server;
@@ -299,7 +298,7 @@ public class ServerDaemon implements Daemon {
     }
 
     private RequestLog createRequestLog() {
-        final NCSARequestLog log = new NCSARequestLog();
+        final ACSRequestLog log = new ACSRequestLog();
         final File logPath = new File(accessLogFile);
         final File parentFile = logPath.getParentFile();
         if (parentFile != null) {
diff --git a/utils/src/main/java/com/cloud/utils/StringUtils.java 
b/utils/src/main/java/com/cloud/utils/StringUtils.java
index 94295e3bb2e..73b8d04bf00 100644
--- a/utils/src/main/java/com/cloud/utils/StringUtils.java
+++ b/utils/src/main/java/com/cloud/utils/StringUtils.java
@@ -25,6 +25,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
 
 import java.nio.charset.Charset;
 import java.util.Arrays;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -39,12 +40,12 @@ import java.util.stream.Collectors;
 public class StringUtils extends org.apache.commons.lang3.StringUtils {
     private static final char[] hexChar = {'0', '1', '2', '3', '4', '5', '6', 
'7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
 
-    private static Charset preferredACSCharset;
+    private static final Charset preferredACSCharset;
     private static final String UTF8 = "UTF-8";
 
     static {
         if (isUtf8Supported()) {
-            preferredACSCharset = Charset.forName(UTF8);
+            preferredACSCharset = StandardCharsets.UTF_8;
         } else {
             preferredACSCharset = Charset.defaultCharset();
         }
@@ -66,8 +67,8 @@ public class StringUtils extends 
org.apache.commons.lang3.StringUtils {
         if (tags != null) {
             final String[] tokens = tags.split(",");
             final StringBuilder t = new StringBuilder();
-            for (int i = 0; i < tokens.length; i++) {
-                t.append(tokens[i].trim()).append(",");
+            for (String token : tokens) {
+                t.append(token.trim()).append(",");
             }
             t.delete(t.length() - 1, t.length());
             tags = t.toString();
@@ -77,16 +78,16 @@ public class StringUtils extends 
org.apache.commons.lang3.StringUtils {
     }
 
     /**
-     * @param tags
+     * @param tags a {code}String{code} containing a list of comma separated 
tags
      * @return List of tags
      */
     public static List<String> csvTagsToList(final String tags) {
-        final List<String> tagsList = new ArrayList<String>();
+        final List<String> tagsList = new ArrayList<>();
 
         if (tags != null) {
             final String[] tokens = tags.split(",");
-            for (int i = 0; i < tokens.length; i++) {
-                tagsList.add(tokens[i].trim());
+            for (String token : tokens) {
+                tagsList.add(token.trim());
             }
         }
 
@@ -95,13 +96,13 @@ public class StringUtils extends 
org.apache.commons.lang3.StringUtils {
 
     /**
      * Converts a List of tags to a comma separated list
-     * @param tagsList
+     * @param tagsList List of tags to convert to a comma separated list in a 
{code}String{code}
      * @return String containing a comma separated list of tags
      */
 
     public static String listToCsvTags(final List<String> tagsList) {
         final StringBuilder tags = new StringBuilder();
-        if (tagsList.size() > 0) {
+        if (!tagsList.isEmpty()) {
             for (int i = 0; i < tagsList.size(); i++) {
                 tags.append(tagsList.get(i));
                 if (i != tagsList.size() - 1) {
@@ -113,22 +114,6 @@ public class StringUtils extends 
org.apache.commons.lang3.StringUtils {
         return tags.toString();
     }
 
-    public static String getExceptionStackInfo(final Throwable e) {
-        final StringBuffer sb = new StringBuffer();
-
-        sb.append(e.toString()).append("\n");
-        final StackTraceElement[] elemnents = e.getStackTrace();
-        for (final StackTraceElement element : elemnents) {
-            sb.append(element.getClassName()).append(".");
-            sb.append(element.getMethodName()).append("(");
-            sb.append(element.getFileName()).append(":");
-            sb.append(element.getLineNumber()).append(")");
-            sb.append("\n");
-        }
-
-        return sb.toString();
-    }
-
     public static String unicodeEscape(final String s) {
         final StringBuilder sb = new StringBuilder();
         for (int i = 0; i < s.length(); i++) {
@@ -151,25 +136,22 @@ public class StringUtils extends 
org.apache.commons.lang3.StringUtils {
             return "*";
         }
 
-        final StringBuffer sb = new StringBuffer();
-        sb.append(password.charAt(0));
-        for (int i = 1; i < password.length(); i++) {
-            sb.append("*");
-        }
-
-        return sb.toString();
+        return password.charAt(0) +
+                "*".repeat(password.length() - 1);
     }
 
     // removes a password request param and it's value, also considering 
password is in query parameter value which has been url encoded
-    private static final Pattern REGEX_PASSWORD_QUERYSTRING = 
Pattern.compile("(&|%26)?[^(&|%26)]*((p|P)assword|accesskey|secretkey)(=|%3D).*?(?=(%26|[&'\"]|$))");
+    private static final Pattern REGEX_PASSWORD_QUERYSTRING = 
Pattern.compile("(&|%26)?[^(&|%26)]*(([pP])assword|accesskey|secretkey)(=|%3D).*?(?=(%26|[&'\"]|$))");
 
     // removes a password/accesskey/ property from a response json object
-    private static final Pattern REGEX_PASSWORD_JSON = 
Pattern.compile("\"((p|P)assword|privatekey|accesskey|secretkey)\":\\s?\".*?\",?");
+    private static final Pattern REGEX_PASSWORD_JSON = 
Pattern.compile("\"(([pP])assword|privatekey|accesskey|secretkey)\":\\s?\".*?\",?");
 
-    private static final Pattern REGEX_PASSWORD_DETAILS = 
Pattern.compile("(&|%26)?details(\\[|%5B)\\d*(\\]|%5D)\\.key(=|%3D)((p|P)assword|accesskey|secretkey)(?=(%26|[&'\"]))");
+    private static final Pattern REGEX_PASSWORD_DETAILS = 
Pattern.compile("(&|%26)?details(\\[|%5B)\\d*(\\]|%5D)\\.key(=|%3D)(([pP])assword|accesskey|secretkey)(?=(%26|[&'\"]))");
 
     private static final Pattern REGEX_PASSWORD_DETAILS_INDEX = 
Pattern.compile("details(\\[|%5B)\\d*(\\]|%5D)");
 
+    private static final Pattern REGEX_SESSION_KEY = 
Pattern.compile("sessionkey=[A-Za-z0-9_-]+");
+
     private static final Pattern REGEX_REDUNDANT_AND = 
Pattern.compile("(&|%26)(&|%26)+");
 
     // Responsible for stripping sensitive content from request and response 
strings
@@ -178,6 +160,7 @@ public class StringUtils extends 
org.apache.commons.lang3.StringUtils {
         if (stringToClean != null) {
             cleanResult = 
REGEX_PASSWORD_QUERYSTRING.matcher(stringToClean).replaceAll("");
             cleanResult = 
REGEX_PASSWORD_JSON.matcher(cleanResult).replaceAll("");
+            cleanResult = 
REGEX_SESSION_KEY.matcher(cleanResult).replaceAll("");
             final Matcher detailsMatcher = 
REGEX_PASSWORD_DETAILS.matcher(cleanResult);
             while (detailsMatcher.find()) {
                 final Matcher detailsIndexMatcher = 
REGEX_PASSWORD_DETAILS_INDEX.matcher(detailsMatcher.group());
@@ -205,24 +188,20 @@ public class StringUtils extends 
org.apache.commons.lang3.StringUtils {
             return true;
         }
 
-        if (tags1 != null && tags2 == null) {
-            return false;
-        }
-
-        if (tags1 == null && tags2 != null) {
+        if (tags1 == null ^ tags2 == null) {
             return false;
         }
 
         final String delimiter = ",";
 
-        final List<String> lstTags1 = new ArrayList<String>();
+        final List<String> lstTags1 = new ArrayList<>();
         final String[] aTags1 = tags1.split(delimiter);
 
         for (final String tag1 : aTags1) {
             lstTags1.add(tag1.toLowerCase());
         }
 
-        final List<String> lstTags2 = new ArrayList<String>();
+        final List<String> lstTags2 = new ArrayList<>();
         final String[] aTags2 = tags2.split(delimiter);
 
         for (final String tag2 : aTags2) {
@@ -233,7 +212,7 @@ public class StringUtils extends 
org.apache.commons.lang3.StringUtils {
     }
 
     public static Map<String, String> stringToMap(final String s) {
-        final Map<String, String> map = new HashMap<String, String>();
+        final Map<String, String> map = new HashMap<>();
         final String[] elements = s.split(";");
         for (final String parts : elements) {
             final String[] keyValue = parts.split(":");
@@ -243,14 +222,14 @@ public class StringUtils extends 
org.apache.commons.lang3.StringUtils {
     }
 
     public static String mapToString(final Map<String, String> map) {
-        String s = "";
+        StringBuilder s = new StringBuilder();
         for (final Map.Entry<String, String> entry : map.entrySet()) {
-            s += entry.getKey() + ":" + entry.getValue() + ";";
+            
s.append(entry.getKey()).append(":").append(entry.getValue()).append(";");
         }
         if (s.length() > 0) {
-            s = s.substring(0, s.length() - 1);
+            s = new StringBuilder(s.substring(0, s.length() - 1));
         }
-        return s;
+        return s.toString();
     }
 
     public static <T> List<T> applyPagination(final List<T> originalList, 
final Long startIndex, final Long pageSizeVal) {
@@ -271,7 +250,7 @@ public class StringUtils extends 
org.apache.commons.lang3.StringUtils {
     }
 
     private static <T> List<List<T>> partitionList(final List<T> originalList, 
final int chunkSize) {
-        final List<List<T>> listOfChunks = new ArrayList<List<T>>();
+        final List<List<T>> listOfChunks = new ArrayList<>();
         for (int i = 0; i < originalList.size() / chunkSize; i++) {
             listOfChunks.add(originalList.subList(i * chunkSize, i * chunkSize 
+ chunkSize));
         }
@@ -299,9 +278,7 @@ public class StringUtils extends 
org.apache.commons.lang3.StringUtils {
         if (org.apache.commons.lang3.StringUtils.isNotBlank(jsonString)) {
             try {
                 JsonNode jsonNode = objectMapper.readTree(jsonString);
-                jsonNode.fields().forEachRemaining(entry -> {
-                    mapResult.put(entry.getKey(), entry.getValue().asText());
-                });
+                jsonNode.fields().forEachRemaining(entry -> 
mapResult.put(entry.getKey(), entry.getValue().asText()));
             } catch (Exception e) {
                 throw new CloudRuntimeException("Error while parsing json to 
convert it to map " + e.getMessage());
             }
diff --git a/utils/src/test/java/com/cloud/utils/StringUtilsTest.java 
b/utils/src/test/java/com/cloud/utils/StringUtilsTest.java
index 8aab74f1134..8a41f956043 100644
--- a/utils/src/test/java/com/cloud/utils/StringUtilsTest.java
+++ b/utils/src/test/java/com/cloud/utils/StringUtilsTest.java
@@ -240,6 +240,21 @@ public class StringUtilsTest {
         assertEquals(result, expected);
     }
 
+    @Test
+    public void testCleanSessionkeyFromAccessLogString() {
+        final String input = "GET 
/client/api/?managementserverid=cad7010f-216f-48cb-af11-280588863c4e&command=readyForShutdown&response=json&sessionkey=-FrgnKy6pj-JB4BI2sXqo
 HTTP/1.1\" 200 180 \"-\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) 
AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Safari/605.1.15 Ddg/18.5";
+        final String expected = "GET 
/client/api/?managementserverid=cad7010f-216f-48cb-af11-280588863c4e&command=readyForShutdown&response=json&
 HTTP/1.1\" 200 180 \"-\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) 
AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Safari/605.1.15 Ddg/18.5";
+        final String result = StringUtils.cleanString(input);
+        assertEquals(expected, result);
+    }
+    @Test
+    public void testCleanSessionkeyFromRequestJsonString() {
+        final String input = "{id=64b5e71d-2ae8-11ef-9466-1e00c400042b, 
showicon=true, command=listUsers, response=json, 
sessionkey=lXfAicKQXPBzt7KjLx6DwVfcOuA}";
+        final String expected = "{id=64b5e71d-2ae8-11ef-9466-1e00c400042b, 
showicon=true, command=listUsers, response=json, }";
+        final String result = StringUtils.cleanString(input);
+        assertEquals(expected, result);
+    }
+
     @Test
     public void listToCsvTags() {
         assertEquals("a,b,c", StringUtils.listToCsvTags(Arrays.asList("a","b", 
"c")));

Reply via email to