From 5f92e84d9dff3a5a060b691e234f0e9106a7fe20 Mon Sep 17 00:00:00 2001 From: Amir Raminfar Date: Wed, 31 May 2023 15:19:40 -0700 Subject: [PATCH] feat: adds support for different std out and err streams (#2229) * feat: adds support for different std out and err streams * feat: adds std to json * fixes tests * fixes deprecated code * fixes download * adds defineEmit as an option * chore: updates modules * adds ui elements * fixes tests * updates languages --- assets/auto-imports.d.ts | 9 +- assets/components.d.ts | 3 + .../components/LogViewer/ComplexLogItem.vue | 3 + .../components/LogViewer/ContainerTitle.vue | 2 +- .../LogViewer/LogActionsToolbar.vue | 47 +- assets/components/LogViewer/LogContainer.vue | 4 +- .../LogViewer/LogEventSource.spec.ts | 41 +- .../components/LogViewer/LogEventSource.vue | 3 +- assets/components/LogViewer/LogStd.vue | 25 + assets/components/LogViewer/SimpleLogItem.vue | 3 + .../__snapshots__/LogEventSource.spec.ts.snap | 7 + assets/components/Tag.vue | 19 + assets/composables/eventsource.ts | 45 +- assets/composables/settings.ts | 8 + assets/composables/storage.ts | 8 +- assets/models/LogEntry.ts | 35 +- assets/pages/settings.vue | 7 +- assets/styles.scss | 35 +- assets/utils/index.ts | 8 - docker/client.go | 64 +- docker/client_test.go | 6 +- docker/log_iterator.go | 14 +- docker/log_iterator_test.go | 8 +- docker/reader.go | 14 +- docker/types.go | 5 +- docs/package.json | 8 +- docs/pnpm-lock.yaml | 248 ++++---- locales/en.yml | 3 + locales/es.yml | 2 + locales/pr.yml | 3 + locales/ru.yml | 3 + package.json | 22 +- pnpm-lock.yaml | 586 ++++++++++++++---- tsconfig.json | 2 +- vite.config.ts | 19 +- web/__snapshots__/web.snapshot | 16 +- web/logs.go | 33 +- web/routes.go | 5 +- web/routes_auth_test.go | 13 +- web/routes_logs_test.go | 50 +- web/routes_test.go | 8 +- 41 files changed, 1061 insertions(+), 383 deletions(-) create mode 100644 assets/components/LogViewer/LogStd.vue create mode 100644 assets/components/Tag.vue diff --git a/assets/auto-imports.d.ts b/assets/auto-imports.d.ts index 281351fb..6aad5f9d 100644 --- a/assets/auto-imports.d.ts +++ b/assets/auto-imports.d.ts @@ -93,7 +93,7 @@ declare global { const onUnmounted: typeof import('vue')['onUnmounted'] const onUpdated: typeof import('vue')['onUpdated'] const pausableWatch: typeof import('@vueuse/core')['pausableWatch'] - const persistentVisibleKeys: typeof import('./utils/index')['persistentVisibleKeys'] + const persistentVisibleKeys: typeof import('./composables/storage')['persistentVisibleKeys'] const provide: typeof import('vue')['provide'] const reactify: typeof import('@vueuse/core')['reactify'] const reactifyObject: typeof import('@vueuse/core')['reactifyObject'] @@ -121,6 +121,7 @@ declare global { const shallowReadonly: typeof import('vue')['shallowReadonly'] const shallowRef: typeof import('vue')['shallowRef'] const showAllContainers: typeof import('./composables/settings')['showAllContainers'] + const showStd: typeof import('./composables/settings')['showStd'] const showTimestamp: typeof import('./composables/settings')['showTimestamp'] const size: typeof import('./composables/settings')['size'] const smallerScrollbars: typeof import('./composables/settings')['smallerScrollbars'] @@ -428,7 +429,7 @@ declare module 'vue' { readonly onUnmounted: UnwrapRef readonly onUpdated: UnwrapRef readonly pausableWatch: UnwrapRef - readonly persistentVisibleKeys: UnwrapRef + readonly persistentVisibleKeys: UnwrapRef readonly provide: UnwrapRef readonly reactify: UnwrapRef readonly reactifyObject: UnwrapRef @@ -456,6 +457,7 @@ declare module 'vue' { readonly shallowReadonly: UnwrapRef readonly shallowRef: UnwrapRef readonly showAllContainers: UnwrapRef + readonly showStd: UnwrapRef readonly showTimestamp: UnwrapRef readonly size: UnwrapRef readonly smallerScrollbars: UnwrapRef @@ -757,7 +759,7 @@ declare module '@vue/runtime-core' { readonly onUnmounted: UnwrapRef readonly onUpdated: UnwrapRef readonly pausableWatch: UnwrapRef - readonly persistentVisibleKeys: UnwrapRef + readonly persistentVisibleKeys: UnwrapRef readonly provide: UnwrapRef readonly reactify: UnwrapRef readonly reactifyObject: UnwrapRef @@ -785,6 +787,7 @@ declare module '@vue/runtime-core' { readonly shallowReadonly: UnwrapRef readonly shallowRef: UnwrapRef readonly showAllContainers: UnwrapRef + readonly showStd: UnwrapRef readonly showTimestamp: UnwrapRef readonly size: UnwrapRef readonly smallerScrollbars: UnwrapRef diff --git a/assets/components.d.ts b/assets/components.d.ts index 33c8675c..0e8d648b 100644 --- a/assets/components.d.ts +++ b/assets/components.d.ts @@ -32,8 +32,10 @@ declare module '@vue/runtime-core' { LogDate: typeof import('./components/LogViewer/LogDate.vue')['default'] LogEventSource: typeof import('./components/LogViewer/LogEventSource.vue')['default'] LogLevel: typeof import('./components/LogViewer/LogLevel.vue')['default'] + LogStd: typeof import('./components/LogViewer/LogStd.vue')['default'] LogViewer: typeof import('./components/LogViewer/LogViewer.vue')['default'] LogViewerWithSource: typeof import('./components/LogViewer/LogViewerWithSource.vue')['default'] + 'Mdi:check': typeof import('~icons/mdi/check')['default'] 'Mdi:dotsVertical': typeof import('~icons/mdi/dots-vertical')['default'] 'Mdi:lightChevronDoubleDown': typeof import('~icons/mdi-light/chevron-double-down')['default'] 'Mdi:lightChevronLeft': typeof import('~icons/mdi-light/chevron-left')['default'] @@ -56,6 +58,7 @@ declare module '@vue/runtime-core' { SkippedEntriesLogItem: typeof import('./components/LogViewer/SkippedEntriesLogItem.vue')['default'] StatMonitor: typeof import('./components/LogViewer/StatMonitor.vue')['default'] StatSparkline: typeof import('./components/LogViewer/StatSparkline.vue')['default'] + Tag: typeof import('./components/Tag.vue')['default'] ZigZag: typeof import('./components/LogViewer/ZigZag.vue')['default'] } } diff --git a/assets/components/LogViewer/ComplexLogItem.vue b/assets/components/LogViewer/ComplexLogItem.vue index 01b74593..d3ab6526 100644 --- a/assets/components/LogViewer/ComplexLogItem.vue +++ b/assets/components/LogViewer/ComplexLogItem.vue @@ -1,5 +1,8 @@ diff --git a/assets/components/LogViewer/LogActionsToolbar.vue b/assets/components/LogViewer/LogActionsToolbar.vue index 87f29303..689b1c32 100644 --- a/assets/components/LogViewer/LogActionsToolbar.vue +++ b/assets/components/LogViewer/LogActionsToolbar.vue @@ -1,6 +1,6 @@ @@ -47,23 +75,14 @@ import { Container } from "@/models/Container"; const { showSearch } = useSearchFilter(); const { base } = config; -const { onClearClicked = (e: Event) => {} } = defineProps<{ - onClearClicked: (e: Event) => void; -}>(); +const clear = defineEmit(); const container = inject("container") as ComputedRef; +const streamConfig = inject("stream-config") as { stdout: boolean; stderr: boolean }; diff --git a/assets/components/LogViewer/LogContainer.vue b/assets/components/LogViewer/LogContainer.vue index 2548e994..52eb4c62 100644 --- a/assets/components/LogViewer/LogContainer.vue +++ b/assets/components/LogViewer/LogContainer.vue @@ -10,7 +10,7 @@
- +
@@ -45,8 +45,10 @@ const emit = defineEmits<{ const store = useContainerStore(); const container = store.currentContainer($$(id)); +const config = reactive({ stdout: true, stderr: true }); provide("container", container); +provide("stream-config", config); const viewer = ref>(); diff --git a/assets/components/LogViewer/LogEventSource.spec.ts b/assets/components/LogViewer/LogEventSource.spec.ts index baeabd46..cdbc0158 100644 --- a/assets/components/LogViewer/LogEventSource.spec.ts +++ b/assets/components/LogViewer/LogEventSource.spec.ts @@ -72,6 +72,7 @@ describe("", () => { }, provide: { container: computed(() => ({ id: "abc", image: "test:v123" })), + "stream-config": reactive({ stdout: true, stderr: true }), scrollingPaused: computed(() => false), }, }, @@ -84,6 +85,8 @@ describe("", () => { }); } + const sourceUrl = "/api/logs/stream?id=abc&lastEventId=&host=localhost&stdout=1&stderr=1"; + test("renders correctly", async () => { const wrapper = createLogEventSource(); expect(wrapper.html()).toMatchSnapshot(); @@ -91,22 +94,22 @@ describe("", () => { test("should connect to EventSource", async () => { const wrapper = createLogEventSource(); - sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitOpen(); - expect(sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].readyState).toBe(1); + sources[sourceUrl].emitOpen(); + expect(sources[sourceUrl].readyState).toBe(1); wrapper.unmount(); }); test("should close EventSource", async () => { const wrapper = createLogEventSource(); - sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitOpen(); + sources[sourceUrl].emitOpen(); wrapper.unmount(); - expect(sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].readyState).toBe(2); + expect(sources[sourceUrl].readyState).toBe(2); }); test("should parse messages", async () => { const wrapper = createLogEventSource(); - sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitOpen(); - sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitMessage({ + sources[sourceUrl].emitOpen(); + sources[sourceUrl].emitMessage({ data: `{"ts":1560336942459, "m":"This is a message.", "id":1}`, }); @@ -121,8 +124,8 @@ describe("", () => { describe("render html correctly", () => { test("should render messages", async () => { const wrapper = createLogEventSource(); - sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitOpen(); - sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitMessage({ + sources[sourceUrl].emitOpen(); + sources[sourceUrl].emitMessage({ data: `{"ts":1560336942459, "m":"This is a message.", "id":1}`, }); @@ -134,8 +137,8 @@ describe("", () => { test("should render messages with color", async () => { const wrapper = createLogEventSource(); - sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitOpen(); - sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitMessage({ + sources[sourceUrl].emitOpen(); + sources[sourceUrl].emitMessage({ data: '{"ts":1560336942459,"m":"\\u001b[30mblack\\u001b[37mwhite", "id":1}', }); @@ -147,8 +150,8 @@ describe("", () => { test("should render messages with html entities", async () => { const wrapper = createLogEventSource(); - sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitOpen(); - sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitMessage({ + sources[sourceUrl].emitOpen(); + sources[sourceUrl].emitMessage({ data: `{"ts":1560336942459, "m":"foo bar", "id":1}`, }); @@ -160,8 +163,8 @@ describe("", () => { test("should render dates with 12 hour style", async () => { const wrapper = createLogEventSource({ hourStyle: "12" }); - sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitOpen(); - sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitMessage({ + sources[sourceUrl].emitOpen(); + sources[sourceUrl].emitMessage({ data: `{"ts":1560336942459, "m":"foo bar", "id":1}`, }); @@ -173,8 +176,8 @@ describe("", () => { test("should render dates with 24 hour style", async () => { const wrapper = createLogEventSource({ hourStyle: "24" }); - sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitOpen(); - sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitMessage({ + sources[sourceUrl].emitOpen(); + sources[sourceUrl].emitMessage({ data: `{"ts":1560336942459, "m":"foo bar", "id":1}`, }); @@ -186,11 +189,11 @@ describe("", () => { test("should render messages with filter", async () => { const wrapper = createLogEventSource({ searchFilter: "test" }); - sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitOpen(); - sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitMessage({ + sources[sourceUrl].emitOpen(); + sources[sourceUrl].emitMessage({ data: `{"ts":1560336942459, "m":"foo bar", "id":1}`, }); - sources["/api/logs/stream?id=abc&lastEventId=&host=localhost"].emitMessage({ + sources[sourceUrl].emitMessage({ data: `{"ts":1560336942459, "m":"test bar", "id":2}`, }); diff --git a/assets/components/LogViewer/LogEventSource.vue b/assets/components/LogViewer/LogEventSource.vue index 9b4ab4cb..dd519e78 100644 --- a/assets/components/LogViewer/LogEventSource.vue +++ b/assets/components/LogViewer/LogEventSource.vue @@ -12,7 +12,8 @@ const emit = defineEmits<{ }>(); const container = inject("container") as ComputedRef; -const { messages, loadOlderLogs } = useLogStream(container); +const config = inject("stream-config") as { stdout: boolean; stderr: boolean }; +const { messages, loadOlderLogs } = useLogStream(container, config); const beforeLoading = () => emit("loading-more", true); const afterLoading = () => emit("loading-more", false); diff --git a/assets/components/LogViewer/LogStd.vue b/assets/components/LogViewer/LogStd.vue new file mode 100644 index 00000000..d07020a1 --- /dev/null +++ b/assets/components/LogViewer/LogStd.vue @@ -0,0 +1,25 @@ + + + + + diff --git a/assets/components/LogViewer/SimpleLogItem.vue b/assets/components/LogViewer/SimpleLogItem.vue index 880cb085..13a2f2d3 100644 --- a/assets/components/LogViewer/SimpleLogItem.vue +++ b/assets/components/LogViewer/SimpleLogItem.vue @@ -1,5 +1,8 @@