mirror of
https://github.com/BlueMap-Minecraft/BlueMap.git
synced 2024-11-13 22:25:57 +01:00
Add advanced webserver logging + settings
This commit is contained in:
parent
8f97b08eb5
commit
d570884def
@ -65,7 +65,7 @@ public BlueMapConfigs(ServerInterface serverInterface, Path defaultDataFolder, P
|
||||
|
||||
this.coreConfig = loadCoreConfig(defaultDataFolder);
|
||||
this.webappConfig = loadWebappConfig(defaultWebroot);
|
||||
this.webserverConfig = loadWebserverConfig(webappConfig.getWebroot());
|
||||
this.webserverConfig = loadWebserverConfig(webappConfig.getWebroot(), coreConfig.getData());
|
||||
this.pluginConfig = usePluginConf ? loadPluginConfig() : new PluginConfig();
|
||||
this.storageConfigs = Collections.unmodifiableMap(loadStorageConfigs(webappConfig.getWebroot()));
|
||||
this.mapConfigs = Collections.unmodifiableMap(loadMapConfigs());
|
||||
@ -144,7 +144,7 @@ private synchronized CoreConfig loadCoreConfig(Path defaultDataFolder) throws Co
|
||||
return configManager.loadConfig(configFileRaw, CoreConfig.class);
|
||||
}
|
||||
|
||||
private synchronized WebserverConfig loadWebserverConfig(Path defaultWebroot) throws ConfigurationException {
|
||||
private synchronized WebserverConfig loadWebserverConfig(Path defaultWebroot, Path dataRoot) throws ConfigurationException {
|
||||
Path configFileRaw = Path.of("webserver");
|
||||
Path configFile = configManager.findConfigPath(configFileRaw);
|
||||
Path configFolder = configFile.getParent();
|
||||
@ -156,6 +156,8 @@ private synchronized WebserverConfig loadWebserverConfig(Path defaultWebroot) th
|
||||
configFolder.resolve("webserver.conf"),
|
||||
configManager.loadConfigTemplate("/de/bluecolored/bluemap/config/webserver.conf")
|
||||
.setVariable("webroot", formatPath(defaultWebroot))
|
||||
.setVariable("logfile", formatPath(dataRoot.resolve("logs").resolve("webapp.log")))
|
||||
.setVariable("logfile-with-time", formatPath(dataRoot.resolve("logs").resolve("webapp_%1$tF_%1$tT.log")))
|
||||
.build(),
|
||||
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING
|
||||
);
|
||||
|
@ -38,13 +38,13 @@
|
||||
public class WebserverConfig {
|
||||
|
||||
private boolean enabled = true;
|
||||
|
||||
private Path webroot = Path.of("bluemap", "web");
|
||||
|
||||
private String ip = "0.0.0.0";
|
||||
|
||||
private int port = 8100;
|
||||
|
||||
private LogConfig log = new LogConfig();
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
@ -71,4 +71,30 @@ public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public LogConfig getLog() {
|
||||
return log;
|
||||
}
|
||||
|
||||
@DebugDump
|
||||
@ConfigSerializable
|
||||
public static class LogConfig {
|
||||
|
||||
private String file = null;
|
||||
private boolean append = true;
|
||||
private String format = "%1$s \"%3$s %4$s %5$s\" %6$s %7$s";
|
||||
|
||||
public String getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
public boolean isAppend() {
|
||||
return append;
|
||||
}
|
||||
|
||||
public String getFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -59,6 +59,9 @@
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Predicate;
|
||||
@ -185,8 +188,22 @@ private void load(@Nullable ResourcePack preloadedResourcePack) throws IOExcepti
|
||||
);
|
||||
}
|
||||
|
||||
// create web-logger
|
||||
List<Logger> webLoggerList = new ArrayList<>();
|
||||
if (webserverConfig.getLog().getFile() != null) {
|
||||
ZonedDateTime zdt = ZonedDateTime.ofInstant(Instant.now(), ZoneId.systemDefault());
|
||||
webLoggerList.add(Logger.file(
|
||||
Path.of(String.format(webserverConfig.getLog().getFile(), zdt)),
|
||||
webserverConfig.getLog().isAppend()
|
||||
));
|
||||
}
|
||||
|
||||
try {
|
||||
webServer = new HttpServer(routingRequestHandler);
|
||||
webServer = new HttpServer(new LoggingRequestHandler(
|
||||
routingRequestHandler,
|
||||
webserverConfig.getLog().getFormat(),
|
||||
Logger.combine(webLoggerList)
|
||||
));
|
||||
webServer.bind(new InetSocketAddress(
|
||||
webserverConfig.resolveIp(),
|
||||
webserverConfig.getPort()
|
||||
|
@ -1,50 +1,74 @@
|
||||
package de.bluecolored.bluemap.common.web;
|
||||
|
||||
import de.bluecolored.bluemap.common.web.http.HttpHeader;
|
||||
import de.bluecolored.bluemap.common.web.http.HttpRequest;
|
||||
import de.bluecolored.bluemap.common.web.http.HttpRequestHandler;
|
||||
import de.bluecolored.bluemap.common.web.http.HttpResponse;
|
||||
import de.bluecolored.bluemap.common.web.http.*;
|
||||
import de.bluecolored.bluemap.core.logger.Logger;
|
||||
|
||||
public class LoggingRequestHandler implements HttpRequestHandler {
|
||||
|
||||
private final HttpRequestHandler delegate;
|
||||
private final Logger logger;
|
||||
private final String format;
|
||||
|
||||
public LoggingRequestHandler(HttpRequestHandler delegate) {
|
||||
this(delegate, Logger.global);
|
||||
}
|
||||
|
||||
public LoggingRequestHandler(HttpRequestHandler delegate, Logger logger) {
|
||||
this(delegate, "", logger);
|
||||
}
|
||||
|
||||
public LoggingRequestHandler(HttpRequestHandler delegate, String format) {
|
||||
this(delegate, format, Logger.global);
|
||||
}
|
||||
|
||||
public LoggingRequestHandler(HttpRequestHandler delegate, String format, Logger logger) {
|
||||
this.delegate = delegate;
|
||||
this.format = format;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpResponse handle(HttpRequest request) {
|
||||
String source = request.getSource().toString();
|
||||
|
||||
// gather format parameters from request
|
||||
String source = request.getSource().toString();
|
||||
String xffSource = source;
|
||||
HttpHeader xffHeader = request.getHeader("X-Forwarded-For");
|
||||
if (xffHeader != null && !xffHeader.getValues().isEmpty()) {
|
||||
source = xffHeader.getValues().get(0);
|
||||
xffSource = xffHeader.getValues().get(0);
|
||||
}
|
||||
|
||||
StringBuilder log = new StringBuilder()
|
||||
.append(source)
|
||||
.append(" \"").append(request.getMethod())
|
||||
.append(" ").append(request.getAddress())
|
||||
.append(" ").append(request.getVersion())
|
||||
.append("\" ");
|
||||
String method = request.getMethod();
|
||||
String address = request.getAddress();
|
||||
String version = request.getVersion();
|
||||
|
||||
// run request
|
||||
HttpResponse response = delegate.handle(request);
|
||||
|
||||
log.append(response.getStatusCode());
|
||||
if (response.getStatusCode().getCode() < 400) {
|
||||
logger.logInfo(log.toString());
|
||||
// gather format parameters from response
|
||||
HttpStatusCode status = response.getStatusCode();
|
||||
int statusCode = status.getCode();
|
||||
String statusMessage = status.getMessage();
|
||||
|
||||
// format log message
|
||||
String log = String.format(this.format,
|
||||
source,
|
||||
xffSource,
|
||||
method,
|
||||
address,
|
||||
version,
|
||||
statusCode,
|
||||
statusMessage
|
||||
);
|
||||
|
||||
// do the logging
|
||||
if (statusCode < 500) {
|
||||
logger.logInfo(log);
|
||||
} else {
|
||||
logger.logWarning(log.toString());
|
||||
logger.logWarning(log);
|
||||
}
|
||||
|
||||
// return the response
|
||||
return response;
|
||||
}
|
||||
|
||||
|
@ -16,3 +16,30 @@ webroot: "${webroot}"
|
||||
# The port that the webserver listens to.
|
||||
# Default is 8100
|
||||
port: 8100
|
||||
|
||||
# Config-section for webserver-activity logging
|
||||
log: {
|
||||
# The file where all the webserver-activity will be logged to.
|
||||
# Comment out to disable the logging completely.
|
||||
# Java String formatting syntax can be used to add time
|
||||
# Default is no logging
|
||||
file: "${logfile}"
|
||||
#file: "${logfile-with-time}"
|
||||
|
||||
# Whether the logger should append to an existing file, or overwrite it
|
||||
# Default is true
|
||||
append: true
|
||||
|
||||
# The format of the webserver-acivity logs.
|
||||
# The syntax is the java String formatting, see: https://docs.oracle.com/javase/8/docs/api/java/util/Formatter.html
|
||||
# Possible Arguments:
|
||||
# 1 - the source address (ignoring any xff headers)
|
||||
# 2 - the source address (using the (leftmost) xff header if provided)
|
||||
# 3 - the http-method of the request
|
||||
# 4 - the full request-address
|
||||
# 5 - the protocol version of the request
|
||||
# 6 - the status-code of the response
|
||||
# 7 - the status-message of the response
|
||||
# Default is "%1$s \"%3$s %4$s %5$s\" %6$s %7$s"
|
||||
format: "%1$s \"%3$s %4$s %5$s\" %6$s %7$s"
|
||||
}
|
||||
|
@ -0,0 +1,44 @@
|
||||
package de.bluecolored.bluemap.core.logger;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class JavaLogger extends AbstractLogger {
|
||||
|
||||
private final Logger out;
|
||||
|
||||
public JavaLogger(Logger out) {
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logError(String message, Throwable throwable) {
|
||||
out.log(Level.SEVERE, message, throwable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logWarning(String message) {
|
||||
out.log(Level.WARNING, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logInfo(String message) {
|
||||
out.log(Level.INFO, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logDebug(String message) {
|
||||
if (out.isLoggable(Level.FINE)) out.log(Level.FINE, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void noFloodDebug(String message) {
|
||||
if (out.isLoggable(Level.FINE)) super.noFloodDebug(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void noFloodDebug(String key, String message) {
|
||||
if (out.isLoggable(Level.FINE)) super.noFloodDebug(key, message);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package de.bluecolored.bluemap.core.logger;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.logging.LogRecord;
|
||||
|
||||
public class LogFormatter extends Formatter {
|
||||
|
||||
private final String format;
|
||||
|
||||
public LogFormatter() {
|
||||
this("[%1$tF %1$tT][%4$s] %5$s%6$s%n");
|
||||
}
|
||||
|
||||
public LogFormatter(String format) {
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Taken from {@link java.util.logging.SimpleFormatter} to be able to overwrite the format string.
|
||||
* @param record the log record to be formatted.
|
||||
* @return the formatted log
|
||||
*/
|
||||
public String format(LogRecord record) {
|
||||
ZonedDateTime zdt = ZonedDateTime.ofInstant(
|
||||
record.getInstant(), ZoneId.systemDefault());
|
||||
String source;
|
||||
if (record.getSourceClassName() != null) {
|
||||
source = record.getSourceClassName();
|
||||
if (record.getSourceMethodName() != null) {
|
||||
source += " " + record.getSourceMethodName();
|
||||
}
|
||||
} else {
|
||||
source = record.getLoggerName();
|
||||
}
|
||||
String message = formatMessage(record);
|
||||
String throwable = "";
|
||||
if (record.getThrown() != null) {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
pw.println();
|
||||
record.getThrown().printStackTrace(pw);
|
||||
pw.close();
|
||||
throwable = sw.toString();
|
||||
}
|
||||
return String.format(format,
|
||||
zdt,
|
||||
source,
|
||||
record.getLoggerName(),
|
||||
record.getLevel().getLocalizedName(),
|
||||
message,
|
||||
throwable);
|
||||
}
|
||||
|
||||
}
|
@ -24,6 +24,14 @@
|
||||
*/
|
||||
package de.bluecolored.bluemap.core.logger;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.logging.FileHandler;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
public abstract class Logger {
|
||||
|
||||
public static Logger global = stdOut();
|
||||
@ -107,4 +115,41 @@ public static Logger stdOut(){
|
||||
return new PrintStreamLogger(System.out, System.err);
|
||||
}
|
||||
|
||||
|
||||
public static Logger file(Path path) throws IOException {
|
||||
return file(path, null);
|
||||
}
|
||||
|
||||
public static Logger file(Path path, boolean append) throws IOException {
|
||||
return file(path, null, append);
|
||||
}
|
||||
|
||||
public static Logger file(Path path, String format) throws IOException {
|
||||
return file(path, format, true);
|
||||
}
|
||||
|
||||
public static Logger file(Path path, @Nullable String format, boolean append) throws IOException {
|
||||
Files.createDirectories(path.getParent());
|
||||
|
||||
FileHandler fileHandler = new FileHandler(path.toString(), append);
|
||||
fileHandler.setFormatter(format == null ? new LogFormatter() : new LogFormatter(format));
|
||||
|
||||
java.util.logging.Logger javaLogger = java.util.logging.Logger.getAnonymousLogger();
|
||||
javaLogger.setUseParentHandlers(false);
|
||||
javaLogger.addHandler(fileHandler);
|
||||
|
||||
return new JavaLogger(javaLogger);
|
||||
}
|
||||
|
||||
public static Logger combine(Iterable<Logger> logger) {
|
||||
return combine(StreamSupport.stream(logger.spliterator(), false)
|
||||
.toArray(Logger[]::new));
|
||||
}
|
||||
|
||||
public static Logger combine(Logger... logger) {
|
||||
if (logger.length == 0) return new VoidLogger();
|
||||
if (logger.length == 1) return logger[0];
|
||||
return new MultiLogger(logger);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,36 @@
|
||||
package de.bluecolored.bluemap.core.logger;
|
||||
|
||||
@SuppressWarnings("ForLoopReplaceableByForEach")
|
||||
public class MultiLogger extends AbstractLogger {
|
||||
|
||||
private final Logger[] logger;
|
||||
|
||||
public MultiLogger(Logger... logger) {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logError(String message, Throwable throwable) {
|
||||
for (int i = 0; i < logger.length; i++)
|
||||
logger[i].logError(message, throwable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logWarning(String message) {
|
||||
for (int i = 0; i < logger.length; i++)
|
||||
logger[i].logWarning(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logInfo(String message) {
|
||||
for (int i = 0; i < logger.length; i++)
|
||||
logger[i].logInfo(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logDebug(String message) {
|
||||
for (int i = 0; i < logger.length; i++)
|
||||
logger[i].logDebug(message);
|
||||
}
|
||||
|
||||
}
|
@ -58,6 +58,9 @@
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Pattern;
|
||||
@ -203,10 +206,25 @@ public void startWebserver(BlueMapService blueMap, boolean verbose) throws IOExc
|
||||
);
|
||||
}
|
||||
|
||||
List<Logger> webLoggerList = new ArrayList<>();
|
||||
if (verbose) webLoggerList.add(Logger.global);
|
||||
if (config.getLog().getFile() != null) {
|
||||
ZonedDateTime zdt = ZonedDateTime.ofInstant(Instant.now(), ZoneId.systemDefault());
|
||||
webLoggerList.add(Logger.file(
|
||||
Path.of(String.format(config.getLog().getFile(), zdt)),
|
||||
config.getLog().isAppend()
|
||||
));
|
||||
}
|
||||
|
||||
HttpRequestHandler handler = new BlueMapResponseModifier(routingRequestHandler);
|
||||
if (verbose) handler = new LoggingRequestHandler(handler);
|
||||
handler = new LoggingRequestHandler(
|
||||
handler,
|
||||
config.getLog().getFormat(),
|
||||
Logger.combine(webLoggerList)
|
||||
);
|
||||
|
||||
try {
|
||||
//noinspection resource
|
||||
HttpServer webServer = new HttpServer(handler);
|
||||
webServer.bind(new InetSocketAddress(
|
||||
config.resolveIp(),
|
||||
|
Loading…
Reference in New Issue
Block a user