diff --git a/spring-boot-admin-samples/spring-boot-admin-sample-servlet/src/main/java/de/codecentric/boot/admin/sample/QuartzJobsConfiguration.java b/spring-boot-admin-samples/spring-boot-admin-sample-servlet/src/main/java/de/codecentric/boot/admin/sample/QuartzJobsConfiguration.java index bc103136749..734c6b60521 100644 --- a/spring-boot-admin-samples/spring-boot-admin-sample-servlet/src/main/java/de/codecentric/boot/admin/sample/QuartzJobsConfiguration.java +++ b/spring-boot-admin-samples/spring-boot-admin-sample-servlet/src/main/java/de/codecentric/boot/admin/sample/QuartzJobsConfiguration.java @@ -24,6 +24,8 @@ import org.quartz.SimpleScheduleBuilder; import org.quartz.Trigger; import org.quartz.TriggerBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.quartz.QuartzJobBean; @@ -108,9 +110,11 @@ public Trigger hourlyTestTrigger() { */ public static class SampleJob extends QuartzJobBean { + private static final Logger logger = LoggerFactory.getLogger(SampleJob.class); + @Override protected void executeInternal(org.quartz.JobExecutionContext context) { - System.out.println("Sample Quartz Job executed at " + new java.util.Date()); + logger.info("Sample Quartz Job executed at {}", new java.util.Date()); } } @@ -120,9 +124,11 @@ protected void executeInternal(org.quartz.JobExecutionContext context) { */ public static class AnotherSampleJob extends QuartzJobBean { + private static final Logger logger = LoggerFactory.getLogger(AnotherSampleJob.class); + @Override protected void executeInternal(org.quartz.JobExecutionContext context) { - System.out.println("Another Quartz Job executed at " + new java.util.Date()); + logger.info("Another Quartz Job executed at {}", new java.util.Date()); } } diff --git a/spring-boot-admin-server-ui/src/main/frontend/utils/logtail.ts b/spring-boot-admin-server-ui/src/main/frontend/utils/logtail.ts index f0469aaa215..5e5d0c6c076 100644 --- a/spring-boot-admin-server-ui/src/main/frontend/utils/logtail.ts +++ b/spring-boot-admin-server-ui/src/main/frontend/utils/logtail.ts @@ -18,6 +18,7 @@ import { EMPTY, Observable, catchError, concatMap, of, timer } from './rxjs'; export default (getFn, interval, initialSize = 300 * 1024) => { let range = `bytes=-${initialSize}`; let size = 0; + let atTheEnd = false; return timer(0, interval).pipe( concatMap(() => { @@ -46,7 +47,10 @@ export default (getFn, interval, initialSize = 300 * 1024) => { size = contentLength; range = `bytes=${size - 1}-`; } else if (response.status === 206) { - size = parseInt(response.headers['content-range'].split('/')[1]); + const contentRangeParts = response.headers['content-range'].split('/'); + size = parseInt(contentRangeParts[1]); + // The end value of the range is always one byte less than the size when at the end + atTheEnd = parseInt(contentRangeParts[0].split('-')[1]) == size - 1; range = `bytes=${size - 1}-`; } else if (response.status === 416) { size = 0; @@ -68,7 +72,7 @@ export default (getFn, interval, initialSize = 300 * 1024) => { skipped = size - addendum.length; } } else if (response.data.length > 1) { - // Remove the first byte which has been part of the previos response. + // Remove the first byte which has been part of the previous response. addendum = response.data.substring(1); } @@ -76,7 +80,9 @@ export default (getFn, interval, initialSize = 300 * 1024) => { ? of({ totalBytes: size, skipped, - addendum, + // The log file always temporarily ends with a new line until the next one is written. + // Therefore, if we're at the end of it, we drop such a new line. + addendum: atTheEnd ? addendum.trimEnd() : addendum, }) : EMPTY; }), diff --git a/spring-boot-admin-server-ui/src/main/frontend/views/instances/logfile/index.vue b/spring-boot-admin-server-ui/src/main/frontend/views/instances/logfile/index.vue index 3064d11e958..0e906e31e4e 100644 --- a/spring-boot-admin-server-ui/src/main/frontend/views/instances/logfile/index.vue +++ b/spring-boot-admin-server-ui/src/main/frontend/views/instances/logfile/index.vue @@ -95,7 +95,7 @@ :class="{ 'wrap-lines': wrapLines }" class="log-viewer overflow-x-auto text-sm -mx-6 -my-20 pt-14" > - +
@@ -191,14 +191,20 @@ export default { .subscribe({ next: (lines) => { this.hasLoaded = true; + const logContainer = this.$refs.logContainer; lines.forEach((line) => { + let content; + if (line) { + content = document.createElement('pre'); + content.innerHTML = autolink(this.ansiUp.ansi_to_html(line)); + } else { + content = document.createElement('br'); + } const row = document.createElement('tr'); const col = document.createElement('td'); - const pre = document.createElement('pre'); - pre.innerHTML = autolink(this.ansiUp.ansi_to_html(line)); - col.appendChild(pre); + col.appendChild(content); row.appendChild(col); - document.querySelector('.log-viewer > table')?.appendChild(row); + logContainer.appendChild(row); }); if (this.atBottom) { @@ -269,15 +275,24 @@ export default { max-height: 100%; } +.log-viewer tr, +.log-viewer td { + @apply w-full; +} + .log-viewer pre { padding: 0 0.75em; margin-bottom: 1px; } -.log-viewer pre:hover { +.log-viewer td:hover { background: #dbdbdb; } +.log-viewer a[href] { + @apply underline; +} + .log-viewer.wrap-lines pre { @apply whitespace-pre-wrap; word-break: break-all;