Improve logging of changes that trigger a DevTools upload or restart

Closes gh-31579
This commit is contained in:
Andy Wilkinson 2022-07-28 14:13:23 +01:00
parent 9a2ad6d42d
commit 348b2c28e9
3 changed files with 70 additions and 15 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,6 +20,9 @@ import java.io.File;
import java.net.URL;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@ -44,6 +47,7 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.GenericApplicationListener;
import org.springframework.core.ResolvableType;
import org.springframework.core.log.LogMessage;
import org.springframework.util.StringUtils;
/**
@ -101,13 +105,9 @@ public class LocalDevToolsAutoConfiguration {
}
@Bean
ApplicationListener<ClassPathChangedEvent> restartingClassPathChangedEventListener(
RestartingClassPathChangeChangedEventListener restartingClassPathChangedEventListener(
FileSystemWatcherFactory fileSystemWatcherFactory) {
return (event) -> {
if (event.isRestartRequired()) {
Restarter.getInstance().restart(new FileWatchingFailureHandler(fileSystemWatcherFactory));
}
};
return new RestartingClassPathChangeChangedEventListener(fileSystemWatcherFactory);
}
@Bean
@ -194,4 +194,24 @@ public class LocalDevToolsAutoConfiguration {
}
static class RestartingClassPathChangeChangedEventListener implements ApplicationListener<ClassPathChangedEvent> {
private static final Log logger = LogFactory.getLog(RestartingClassPathChangeChangedEventListener.class);
private final FileSystemWatcherFactory fileSystemWatcherFactory;
RestartingClassPathChangeChangedEventListener(FileSystemWatcherFactory fileSystemWatcherFactory) {
this.fileSystemWatcherFactory = fileSystemWatcherFactory;
}
@Override
public void onApplicationEvent(ClassPathChangedEvent event) {
if (event.isRestartRequired()) {
logger.info(LogMessage.format("Restarting due to %s", event.overview()));
Restarter.getInstance().restart(new FileWatchingFailureHandler(this.fileSystemWatcherFactory));
}
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2021 the original author or authors.
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,6 +18,8 @@ package org.springframework.boot.devtools.classpath;
import java.util.Set;
import org.springframework.boot.devtools.filewatch.ChangedFile;
import org.springframework.boot.devtools.filewatch.ChangedFile.Type;
import org.springframework.boot.devtools.filewatch.ChangedFiles;
import org.springframework.context.ApplicationEvent;
import org.springframework.core.style.ToStringCreator;
@ -71,4 +73,37 @@ public class ClassPathChangedEvent extends ApplicationEvent {
.append("restartRequired", this.restartRequired).toString();
}
/**
* Return an overview of the changes that triggered this event.
* @return an overview of the changes
* @since 2.6.11
*/
public String overview() {
int added = 0;
int deleted = 0;
int modified = 0;
for (ChangedFiles changedFiles : this.changeSet) {
for (ChangedFile changedFile : changedFiles) {
Type type = changedFile.getType();
if (type == Type.ADD) {
added++;
}
else if (type == Type.DELETE) {
deleted++;
}
else if (type == Type.MODIFY) {
modified++;
}
}
}
int size = added + deleted + modified;
return String.format("%s (%s, %s, %s)", quantityOfUnit(size, "class path change"),
quantityOfUnit(added, "addition"), quantityOfUnit(deleted, "deletion"),
quantityOfUnit(modified, "modification"));
}
private String quantityOfUnit(int quantity, String unit) {
return quantity + " " + ((quantity != 1) ? unit + "s" : unit);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -91,14 +91,15 @@ public class ClassPathChangeUploader implements ApplicationListener<ClassPathCha
try {
ClassLoaderFiles classLoaderFiles = getClassLoaderFiles(event);
byte[] bytes = serialize(classLoaderFiles);
performUpload(classLoaderFiles, bytes);
performUpload(classLoaderFiles, bytes, event);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
private void performUpload(ClassLoaderFiles classLoaderFiles, byte[] bytes) throws IOException {
private void performUpload(ClassLoaderFiles classLoaderFiles, byte[] bytes, ClassPathChangedEvent event)
throws IOException {
try {
while (true) {
try {
@ -107,11 +108,11 @@ public class ClassPathChangeUploader implements ApplicationListener<ClassPathCha
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentLength(bytes.length);
FileCopyUtils.copy(bytes, request.getBody());
logUpload(event);
ClientHttpResponse response = request.execute();
HttpStatus statusCode = response.getStatusCode();
Assert.state(statusCode == HttpStatus.OK,
() -> "Unexpected " + statusCode + " response uploading class files");
logUpload(classLoaderFiles);
return;
}
catch (SocketException ex) {
@ -128,9 +129,8 @@ public class ClassPathChangeUploader implements ApplicationListener<ClassPathCha
}
}
private void logUpload(ClassLoaderFiles classLoaderFiles) {
int size = classLoaderFiles.size();
logger.info(LogMessage.format("Uploaded %s class %s", size, (size != 1) ? "resources" : "resource"));
private void logUpload(ClassPathChangedEvent event) {
logger.info(LogMessage.format("Uploading %s", event.overview()));
}
private byte[] serialize(ClassLoaderFiles classLoaderFiles) throws IOException {