From 348b2c28e91f738989b02c1136699b891c8df91b Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 28 Jul 2022 14:13:23 +0100 Subject: [PATCH] Improve logging of changes that trigger a DevTools upload or restart Closes gh-31579 --- .../LocalDevToolsAutoConfiguration.java | 34 +++++++++++++---- .../classpath/ClassPathChangedEvent.java | 37 ++++++++++++++++++- .../client/ClassPathChangeUploader.java | 14 +++---- 3 files changed, 70 insertions(+), 15 deletions(-) diff --git a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/LocalDevToolsAutoConfiguration.java b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/LocalDevToolsAutoConfiguration.java index 51a7a7809bdc..364e12a127be 100644 --- a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/LocalDevToolsAutoConfiguration.java +++ b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/autoconfigure/LocalDevToolsAutoConfiguration.java @@ -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.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.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 @@ static class RestartConfiguration { } @Bean - ApplicationListener 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 int getOrder() { } + static class RestartingClassPathChangeChangedEventListener implements ApplicationListener { + + 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)); + } + } + + } + } diff --git a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/classpath/ClassPathChangedEvent.java b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/classpath/ClassPathChangedEvent.java index 7d800ba6178f..982a9e471454 100644 --- a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/classpath/ClassPathChangedEvent.java +++ b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/classpath/ClassPathChangedEvent.java @@ -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 @@ 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 String toString() { .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); + } + } diff --git a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/remote/client/ClassPathChangeUploader.java b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/remote/client/ClassPathChangeUploader.java index b32a603d3fb8..2fdf68f20673 100644 --- a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/remote/client/ClassPathChangeUploader.java +++ b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/remote/client/ClassPathChangeUploader.java @@ -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 void onApplicationEvent(ClassPathChangedEvent event) { 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 @@ private void performUpload(ClassLoaderFiles classLoaderFiles, byte[] bytes) thro 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 @@ private void performUpload(ClassLoaderFiles classLoaderFiles, byte[] bytes) thro } } - 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 {