From d98000b35a6097f09bb09bf1bb11ab53e5705cda Mon Sep 17 00:00:00 2001 From: Amir Raminfar Date: Wed, 11 Jun 2025 16:23:48 -0700 Subject: [PATCH] feat: adds the ability to show a specific log from the past with a permanent link (#3958) --- assets/auto-imports.d.ts | 4 + assets/components.d.ts | 11 +- assets/components/ContainerDropdown.vue | 2 +- .../ContainerActionsToolbar.vue | 16 ++- .../ContainerViewer/ContainerTitle.vue | 5 +- .../HistoricalContainerLog.vue | 63 +++++++++ assets/components/HostMenu.vue | 2 +- .../components/LogViewer/ComplexLogItem.vue | 26 ++-- .../components/LogViewer/EventSource.spec.ts | 28 +++- assets/components/LogViewer/EventSource.vue | 60 +++++--- .../components/LogViewer/LoadMoreLogItem.vue | 8 +- assets/components/LogViewer/LogActions.vue | 113 +++++++++++++++ assets/components/LogViewer/LogDetails.vue | 2 +- assets/components/LogViewer/LogItem.vue | 13 +- assets/components/LogViewer/LogList.vue | 4 +- .../LogViewer/LogMessageActions.vue | 44 ------ assets/components/LogViewer/LogViewer.vue | 24 ---- .../LogViewer/MultiContainerActionToolbar.vue | 6 +- assets/components/LogViewer/SimpleLogItem.vue | 8 -- .../__snapshots__/EventSource.spec.ts.snap | 70 +++++++--- assets/components/ScrollableView.vue | 43 +++--- assets/composable/eventStreams.ts | 102 ++++++++------ assets/composable/historicalLogs.ts | 131 ++++++++++++++++++ assets/composable/logContext.ts | 5 +- assets/models/Container.ts | 7 + assets/models/LogEntry.ts | 1 + .../pages/container/[id].time.[datetime].vue | 36 +++++ assets/pages/container/[id].vue | 2 +- assets/typed-router.d.ts | 1 + .../dark-homepage-1-Mobile-Chrome-linux.png | Bin 20436 -> 20511 bytes .../dark-homepage-1-chromium-linux.png | Bin 21739 -> 21797 bytes ...default-homepage-1-Mobile-Chrome-linux.png | Bin 19322 -> 19336 bytes .../default-homepage-1-chromium-linux.png | Bin 21299 -> 21304 bytes internal/web/logs.go | 36 ++++- internal/web/logs_test.go | 15 +- locales/en.yml | 2 +- package.json | 1 + pnpm-lock.yaml | 10 ++ 38 files changed, 669 insertions(+), 232 deletions(-) create mode 100644 assets/components/ContainerViewer/HistoricalContainerLog.vue create mode 100644 assets/components/LogViewer/LogActions.vue delete mode 100644 assets/components/LogViewer/LogMessageActions.vue create mode 100644 assets/composable/historicalLogs.ts create mode 100644 assets/pages/container/[id].time.[datetime].vue diff --git a/assets/auto-imports.d.ts b/assets/auto-imports.d.ts index 2a23a99c..324e4edc 100644 --- a/assets/auto-imports.d.ts +++ b/assets/auto-imports.d.ts @@ -69,6 +69,7 @@ declare global { const isReadonly: typeof import('vue')['isReadonly'] const isRef: typeof import('vue')['isRef'] const lightTheme: typeof import('./stores/settings')['lightTheme'] + const loadBetween: typeof import('./composable/eventStreams')['loadBetween'] const locale: typeof import('./stores/settings')['locale'] const loggingContextKey: typeof import('./composable/logContext')['loggingContextKey'] const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable'] @@ -239,6 +240,7 @@ declare global { const useGeolocation: typeof import('@vueuse/core')['useGeolocation'] const useGroupedStream: typeof import('./composable/eventStreams')['useGroupedStream'] const useHead: typeof import('@vueuse/head')['useHead'] + const useHistoricalContainerLog: typeof import('./composable/historicalLogs')['useHistoricalContainerLog'] const useHostStream: typeof import('./composable/eventStreams')['useHostStream'] const useHosts: typeof import('./stores/hosts')['useHosts'] const useI18n: typeof import('vue-i18n')['useI18n'] @@ -461,6 +463,7 @@ declare module 'vue' { readonly isReadonly: UnwrapRef readonly isRef: UnwrapRef readonly lightTheme: UnwrapRef + readonly loadBetween: UnwrapRef readonly locale: UnwrapRef readonly loggingContextKey: UnwrapRef readonly makeDestructurable: UnwrapRef @@ -631,6 +634,7 @@ declare module 'vue' { readonly useGeolocation: UnwrapRef readonly useGroupedStream: UnwrapRef readonly useHead: UnwrapRef + readonly useHistoricalContainerLog: UnwrapRef readonly useHostStream: UnwrapRef readonly useHosts: UnwrapRef readonly useI18n: UnwrapRef diff --git a/assets/components.d.ts b/assets/components.d.ts index a1e7f090..f3952a99 100644 --- a/assets/components.d.ts +++ b/assets/components.d.ts @@ -11,7 +11,6 @@ declare module 'vue' { Announcements: typeof import('./components/Announcements.vue')['default'] 'Carbon:caretDown': typeof import('~icons/carbon/caret-down')['default'] 'Carbon:circleSolid': typeof import('~icons/carbon/circle-solid')['default'] - 'Carbon:copyFile': typeof import('~icons/carbon/copy-file')['default'] 'Carbon:information': typeof import('~icons/carbon/information')['default'] 'Carbon:logoKubernetes': typeof import('~icons/carbon/logo-kubernetes')['default'] 'Carbon:macShift': typeof import('~icons/carbon/mac-shift')['default'] @@ -43,25 +42,31 @@ declare module 'vue' { FuzzySearchModal: typeof import('./components/FuzzySearchModal.vue')['default'] GroupedLog: typeof import('./components/GroupedViewer/GroupedLog.vue')['default'] GroupMenu: typeof import('./components/GroupMenu.vue')['default'] + HistoricalContainerLog: typeof import('./components/ContainerViewer/HistoricalContainerLog.vue')['default'] HostIcon: typeof import('./components/common/HostIcon.vue')['default'] HostList: typeof import('./components/HostList.vue')['default'] HostLog: typeof import('./components/HostViewer/HostLog.vue')['default'] HostMenu: typeof import('./components/HostMenu.vue')['default'] 'Ic:sharpKeyboardReturn': typeof import('~icons/ic/sharp-keyboard-return')['default'] IndeterminateBar: typeof import('./components/common/IndeterminateBar.vue')['default'] + 'Ion:ellipsisVertical': typeof import('~icons/ion/ellipsis-vertical')['default'] KeyShortcut: typeof import('./components/common/KeyShortcut.vue')['default'] LabeledInput: typeof import('./components/common/LabeledInput.vue')['default'] Links: typeof import('./components/Links.vue')['default'] LoadMoreLogItem: typeof import('./components/LogViewer/LoadMoreLogItem.vue')['default'] + LogActions: typeof import('./components/LogViewer/LogActions.vue')['default'] LogAnalytics: typeof import('./components/LogViewer/LogAnalytics.vue')['default'] LogDate: typeof import('./components/LogViewer/LogDate.vue')['default'] LogDetails: typeof import('./components/LogViewer/LogDetails.vue')['default'] LogItem: typeof import('./components/LogViewer/LogItem.vue')['default'] LogLevel: typeof import('./components/LogViewer/LogLevel.vue')['default'] LogList: typeof import('./components/LogViewer/LogList.vue')['default'] - LogMessageActions: typeof import('./components/LogViewer/LogMessageActions.vue')['default'] LogStd: typeof import('./components/LogViewer/LogStd.vue')['default'] LogViewer: typeof import('./components/LogViewer/LogViewer.vue')['default'] + 'MaterialSymbols:codeBlocksRounded': typeof import('~icons/material-symbols/code-blocks-rounded')['default'] + 'MaterialSymbols:contentCopy': typeof import('~icons/material-symbols/content-copy')['default'] + 'MaterialSymbols:eyeTracking': typeof import('~icons/material-symbols/eye-tracking')['default'] + 'MaterialSymbols:link': typeof import('~icons/material-symbols/link')['default'] 'MaterialSymbols:logout': typeof import('~icons/material-symbols/logout')['default'] 'MaterialSymbols:terminal': typeof import('~icons/material-symbols/terminal')['default'] 'MaterialSymbolsLight:collapseAll': typeof import('~icons/material-symbols-light/collapse-all')['default'] @@ -75,7 +80,6 @@ declare module 'vue' { 'Mdi:chevronRight': typeof import('~icons/mdi/chevron-right')['default'] 'Mdi:close': typeof import('~icons/mdi/close')['default'] 'Mdi:cog': typeof import('~icons/mdi/cog')['default'] - 'Mdi:contentCopy': typeof import('~icons/mdi/content-copy')['default'] 'Mdi:docker': typeof import('~icons/mdi/docker')['default'] 'Mdi:gauge': typeof import('~icons/mdi/gauge')['default'] 'Mdi:github': typeof import('~icons/mdi/github')['default'] @@ -83,6 +87,7 @@ declare module 'vue' { 'Mdi:hexagonMultiple': typeof import('~icons/mdi/hexagon-multiple')['default'] 'Mdi:key': typeof import('~icons/mdi/key')['default'] 'Mdi:keyboardEsc': typeof import('~icons/mdi/keyboard-esc')['default'] + 'Mdi:lightningBolt': typeof import('~icons/mdi/lightning-bolt')['default'] 'Mdi:magnify': typeof import('~icons/mdi/magnify')['default'] 'Mdi:satelliteVariant': typeof import('~icons/mdi/satellite-variant')['default'] MobileMenu: typeof import('./components/common/MobileMenu.vue')['default'] diff --git a/assets/components/ContainerDropdown.vue b/assets/components/ContainerDropdown.vue index dd8169f7..2b13ae1d 100644 --- a/assets/components/ContainerDropdown.vue +++ b/assets/components/ContainerDropdown.vue @@ -1,7 +1,7 @@