mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
custom AccessLogger (#9733)
* custom AccessLogger Co-authored-by: Daan Hoogland <dahn@apache.org>
This commit is contained in:
parent
e5f61164b3
commit
9712b4d322
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
@ -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());
|
||||
}
|
||||
|
||||
@ -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")));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user