Skip to content

Commit cdc3b9d

Browse files
committed
feat: implement classname shortener and update task display in index.vue
1 parent b6e0ba3 commit cdc3b9d

File tree

3 files changed

+93
-8
lines changed

3 files changed

+93
-8
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { describe, expect, it } from 'vitest';
2+
3+
import { useClassnameShortener } from '@/composables/useClassnameShortener';
4+
5+
describe('useClassnameShortener', () => {
6+
const { truncateClassname } = useClassnameShortener();
7+
8+
it.each`
9+
given | expected
10+
${'com.example.MyService'} | ${'com.example.MyService'}
11+
${'MyTopLevelClass'} | ${'MyTopLevelClass'}
12+
${'com.example.Outer$Inner'} | ${'com.example.Outer$Inner'}
13+
${'org.springframework.boot.web.embedded.tomcat.TomcatWebServer'} | ${'o.s.b.w.embedded.tomcat.TomcatWebServer'}
14+
`('$given => $expected', ({ given, expected }) => {
15+
expect(truncateClassname(given)).toEqual(expected);
16+
});
17+
});
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
type Options = {
2+
maxLen?: number;
3+
};
4+
5+
export const useClassnameShortener = (options?: Options) => {
6+
return {
7+
truncateClassname: (fqcn: string) =>
8+
abbreviateLoggerName(fqcn, options?.maxLen),
9+
};
10+
};
11+
12+
/**
13+
* Abbreviate a fully qualified Java class name like Spring Boot logging does.
14+
*
15+
* Examples:
16+
* - org.springframework.boot.web.embedded.tomcat.TomcatWebServer
17+
* -> o.s.b.w.e.tomcat.TomcatWebServer
18+
*
19+
* - com.example.very.long.package.name.MyService with maxLen=30
20+
* -> c.e.v.l.p.name.MyService (then drops leftmost segments if needed)
21+
*
22+
* @param fqcn Fully-qualified class name (e.g., "org.example.FooBar")
23+
* @param maxLen Maximum length of the resulting string
24+
* @returns Abbreviated name
25+
*/
26+
function abbreviateLoggerName(fqcn: string, maxLen = 40): string {
27+
const input = (fqcn || '').trim();
28+
if (!input) return '';
29+
30+
if (input.length <= maxLen) return input; // already fits
31+
32+
const parts = input.split('.');
33+
if (parts.length === 1) {
34+
// No package, just crop if needed
35+
return input.slice(input.length - maxLen);
36+
}
37+
38+
const simpleName = parts[parts.length - 1];
39+
let packages = parts.slice(0, -1);
40+
41+
// 1. Start with full name
42+
let out = packages.join('.') + '.' + simpleName;
43+
44+
// 2. Abbreviate left-to-right
45+
for (let i = 0; i < packages.length && out.length > maxLen; i++) {
46+
packages[i] = packages[i].charAt(0); // abbreviate one package
47+
out = packages.join('.') + '.' + simpleName;
48+
if (out.length <= maxLen) return out;
49+
}
50+
51+
// 3. If still too long, drop leftmost package segments
52+
while (packages.length > 0 && out.length > maxLen) {
53+
packages = packages.slice(1);
54+
out = packages.join('.') + (packages.length ? '.' : '') + simpleName;
55+
if (out.length <= maxLen) return out;
56+
}
57+
58+
// 4. Fallback: left-crop
59+
return out.slice(out.length - maxLen);
60+
}

spring-boot-admin-server-ui/src/main/frontend/views/instances/scheduledtasks/index.vue

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@
4242
<tr>
4343
<td
4444
class="scheduledtasks__target"
45-
v-text="task.runnable.target"
45+
:title="task.runnable.target"
46+
v-text="truncateClassname(task.runnable.target)"
4647
/>
4748
<td
4849
class="font-mono text-sm"
@@ -86,7 +87,11 @@
8687
</thead>
8788
<tbody v-for="task in fixedDelay" :key="task.runnable.target">
8889
<tr>
89-
<td v-text="task.runnable.target" />
90+
<td
91+
class="scheduledtasks__target"
92+
:title="task.runnable.target"
93+
v-text="truncateClassname(task.runnable.target)"
94+
/>
9095
<td
9196
:title="`${task.initialDelay}ms`"
9297
v-text="formatTime(task.initialDelay)"
@@ -132,7 +137,11 @@
132137
</thead>
133138
<tbody v-for="task in fixedRate" :key="task.runnable.target">
134139
<tr>
135-
<td v-text="task.runnable.target" />
140+
<td
141+
class="scheduledtasks__target"
142+
:title="task.runnable.target"
143+
v-text="truncateClassname(task.runnable.target)"
144+
/>
136145
<td
137146
:title="`${task.initialDelay}ms`"
138147
v-text="formatTime(task.initialDelay)"
@@ -157,6 +166,7 @@ import { useI18n } from 'vue-i18n';
157166
158167
import SbaPanel from '@/components/sba-panel.vue';
159168
169+
import { useClassnameShortener } from '@/composables/useClassnameShortener';
160170
import Instance from '@/services/instance';
161171
import { usePrettyTime } from '@/utils/prettyTime';
162172
import { VIEW_GROUP } from '@/views/ViewGroup';
@@ -178,9 +188,11 @@ export default {
178188
setup() {
179189
const { formatTime } = usePrettyTime();
180190
const { locale } = useI18n();
191+
const { truncateClassname } = useClassnameShortener({ maxLen: 80 });
181192
182193
return {
183194
formatTime,
195+
truncateClassname,
184196
formatCron: (cron) =>
185197
cronstrue.toString(cron, {
186198
verbose: true,
@@ -249,12 +261,8 @@ export default {
249261
width: 250px;
250262
max-width: 750px;
251263
overflow: hidden;
264+
direction: rtl;
252265
text-overflow: ellipsis;
253266
white-space: nowrap;
254267
}
255-
.scheduledtasks__target:hover {
256-
height: auto;
257-
overflow: visible;
258-
white-space: normal;
259-
}
260268
</style>

0 commit comments

Comments
 (0)