mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-07 17:34:23 +00:00
f1f64ec7f6
* feat: show container name in structured log output Prepend a normalized container name prefix to each line in ComposeService.streamLogs() so both the structured log viewer and the raw terminal identify which container produced each entry. - Backend: prepend displayName (normalized via normalizeContainerName) before LogFormatter.process() in sendOutput and flushBuffer. - LogFormatter: refactor process() to handle both prefix-first and timestamp-first input orders via a while-loop; widen PREFIX_REGEX to accept dotted service names. - Frontend: add containerName to LogRow, extract prefix in parseLine, render as an inline mono chip in the message column, and include the name in downloaded logs (omitting the bracket prefix when null). - Tests: 14 new tests across log-formatter, compose-service streamLogs, and StructuredLogViewer chip rendering + download formatting. * fix: guard LogFormatter loop to at most one prefix and one timestamp The while-loop refactored for order-agnostic prefix/timestamp parsing could continue matching beyond the intended single prefix and timestamp. A log line like "redis | 2024-...Z api | started" would falsely colorize "api |" as a second container prefix in raw terminal output. Add prefixFound/timestampFound boolean guards so the loop stops after one prefix and one timestamp, regardless of input order. * feat: per-service color alternation for log container chips Add an Appearance setting that lets users switch between unified cyan and per-service label-token colors for the container name chips in the structured log viewer. - Extract HUE_VARS and hashLabel() from NodeLabelPill into a shared utility at frontend/src/lib/label-colors.ts. - Add useLogChipColorMode hook (browser-local localStorage, sencho.log-chip-color-mode key, unified by default). - Add SegmentedControl in Settings > Appearance > Display. - Apply inline label-token styles via style attribute in per-service mode; keep current text-brand/80 bg-brand/10 classes in unified mode. - 14 new tests across label-colors, hook, and viewer chip rendering.
68 lines
2.8 KiB
TypeScript
68 lines
2.8 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { LogFormatter } from '../services/LogFormatter';
|
|
|
|
const CYAN = '\x1b[36m';
|
|
const GRAY = '\x1b[90m';
|
|
const RESET = '\x1b[0m';
|
|
const WHITE = '\x1b[37m';
|
|
|
|
describe('LogFormatter.process', () => {
|
|
it('colorizes prefix and timestamp when prefix comes first', () => {
|
|
const result = LogFormatter.process('redis | 2024-01-01T00:00:00Z started');
|
|
expect(result).toContain(`${CYAN}redis${WHITE}${RESET} | `);
|
|
expect(result).toContain(`${GRAY}2024-01-01T00:00:00Z ${RESET}`);
|
|
});
|
|
|
|
it('colorizes timestamp and prefix when timestamp comes first (legacy order)', () => {
|
|
const result = LogFormatter.process('2024-01-01T00:00:00Z redis | started');
|
|
expect(result).toContain(`${GRAY}2024-01-01T00:00:00Z ${RESET}`);
|
|
expect(result).toContain(`${CYAN}redis${WHITE}${RESET} | `);
|
|
});
|
|
|
|
it('colorizes timestamp only when no prefix is present (backward compat)', () => {
|
|
const result = LogFormatter.process('2024-01-01T00:00:00Z started');
|
|
expect(result).toContain(`${GRAY}2024-01-01T00:00:00Z ${RESET}`);
|
|
expect(result).not.toContain(CYAN);
|
|
});
|
|
|
|
it('colorizes dotted container names', () => {
|
|
const result = LogFormatter.process('api.v1 | 2024-01-01T00:00:00Z started');
|
|
expect(result).toContain(`${CYAN}api.v1${WHITE}${RESET} | `);
|
|
expect(result).toContain(`${GRAY}2024-01-01T00:00:00Z ${RESET}`);
|
|
});
|
|
|
|
it('colorizes container names with underscores', () => {
|
|
const result = LogFormatter.process('my-service_1 | 2024-01-01T00:00:00Z started');
|
|
expect(result).toContain(`${CYAN}my-service_1${WHITE}${RESET} | `);
|
|
expect(result).toContain(`${GRAY}2024-01-01T00:00:00Z ${RESET}`);
|
|
});
|
|
|
|
it('handles timestamp with offset notation', () => {
|
|
const result = LogFormatter.process('redis | 2024-01-01T00:00:00+05:00 started');
|
|
expect(result).toContain(`${CYAN}redis${WHITE}${RESET} | `);
|
|
expect(result).toContain(`${GRAY}2024-01-01T00:00:00+05:00 ${RESET}`);
|
|
});
|
|
|
|
it('returns empty string unchanged', () => {
|
|
expect(LogFormatter.process('')).toBe('');
|
|
});
|
|
|
|
it('returns whitespace-only string unchanged', () => {
|
|
expect(LogFormatter.process(' ')).toBe(' ');
|
|
});
|
|
|
|
it('handles bare message with no prefix or timestamp', () => {
|
|
const result = LogFormatter.process('just a plain message');
|
|
expect(result).toBe('just a plain message');
|
|
});
|
|
|
|
it('does not colorize a second "word | " in the message body as a prefix', () => {
|
|
const result = LogFormatter.process('redis | 2024-01-01T00:00:00Z api | started');
|
|
// The first "redis | " is the genuine container prefix.
|
|
expect(result).toContain(`${CYAN}redis${WHITE}${RESET} | `);
|
|
// The "api | " in the body must not be colorized as a second prefix.
|
|
const afterPrefix = result.split(' | ').slice(1).join(' | ');
|
|
expect(afterPrefix).not.toContain(CYAN);
|
|
});
|
|
});
|