mirror of
https://github.com/amir20/dozzle.git
synced 2025-12-25 06:49:23 +01:00
feat: shows raw json in complex logs with option to copy (#3251)
This commit is contained in:
1
assets/components.d.ts
vendored
1
assets/components.d.ts
vendored
@@ -61,6 +61,7 @@ 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:hamburgerMenu': typeof import('~icons/mdi/hamburger-menu')['default']
|
||||
'Mdi:hexagonMultiple': typeof import('~icons/mdi/hexagon-multiple')['default']
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<template>
|
||||
<div class="group/item relative flex w-full gap-x-2 hover:bg-secondary/10" @click="showLogDetails(logEntry)">
|
||||
<div
|
||||
class="group/item relative flex w-full cursor-pointer gap-x-2 hover:bg-secondary/10"
|
||||
@click="showLogDetails(logEntry)"
|
||||
>
|
||||
<div v-if="showContainerName">
|
||||
<ContainerName :id="logEntry.containerID" />
|
||||
</div>
|
||||
@@ -13,7 +16,7 @@
|
||||
<LogLevel :level="logEntry.level" />
|
||||
</div>
|
||||
<div>
|
||||
<ul class="fields cursor-pointer space-x-4">
|
||||
<ul class="fields space-x-4">
|
||||
<li v-for="(value, name) in validValues" :key="name">
|
||||
<span class="text-light">{{ name }}=</span><span class="font-bold" v-if="value === null"><null></span>
|
||||
<template v-else-if="Array.isArray(value)">
|
||||
@@ -24,11 +27,6 @@
|
||||
<li class="text-light" v-if="Object.keys(validValues).length === 0">all values are hidden</li>
|
||||
</ul>
|
||||
</div>
|
||||
<LogMessageActions
|
||||
class="duration-250 absolute -right-1 opacity-0 transition-opacity delay-150 group-hover/entry:opacity-100"
|
||||
:message="() => JSON.stringify(logEntry.message)"
|
||||
:log-entry="logEntry"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
|
||||
@@ -24,6 +24,22 @@
|
||||
<div class="truncate text-lg font-bold">{{ container.image }}</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="flex flex-col gap-2">
|
||||
<div class="flex gap-2 font-thin">
|
||||
Raw JSON
|
||||
|
||||
<UseClipboard v-slot="{ copy, copied }" :source="JSON.stringify(entry.unfilteredMessage)">
|
||||
<button class="swap swap-flip outline-none" @click="copy()" :class="{ 'hover:swap-active': copied }">
|
||||
<mdi:check class="swap-on" />
|
||||
<mdi:content-copy class="swap-off" />
|
||||
</button>
|
||||
</UseClipboard>
|
||||
</div>
|
||||
<div class="max-h-48 overflow-scroll rounded border border-base-lighter bg-base-darker p-2">
|
||||
<pre v-html="syntaxHighlight(entry.unfilteredMessage)"></pre>
|
||||
</div>
|
||||
</section>
|
||||
<table class="table table-pin-rows table-fixed" v-if="entry instanceof ComplexLogEntry">
|
||||
<caption class="caption-bottom">
|
||||
Fields are sortable by dragging and dropping.
|
||||
@@ -55,6 +71,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ComplexLogEntry } from "@/models/LogEntry";
|
||||
import { useSortable } from "@vueuse/integrations/useSortable";
|
||||
import { UseClipboard } from "@vueuse/components";
|
||||
|
||||
const { entry } = defineProps<{ entry: ComplexLogEntry }>();
|
||||
const { currentContainer } = useContainerStore();
|
||||
@@ -115,6 +132,28 @@ const fields = computed({
|
||||
},
|
||||
});
|
||||
|
||||
function syntaxHighlight(json: any) {
|
||||
json = JSON.stringify(json, null, 2);
|
||||
return json.replace(
|
||||
/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|\b\d+\b)/g,
|
||||
function (match: string) {
|
||||
var cls = "json-number";
|
||||
if (/^"/.test(match)) {
|
||||
if (/:$/.test(match)) {
|
||||
cls = "json-key";
|
||||
} else {
|
||||
cls = "json-string";
|
||||
}
|
||||
} else if (/true|false/.test(match)) {
|
||||
cls = "json-boolean";
|
||||
} else if (/null/.test(match)) {
|
||||
cls = "json-null";
|
||||
}
|
||||
return '<span class="' + cls + '">' + match + "</span>";
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
useSortable(list, fields);
|
||||
</script>
|
||||
<style lang="postcss" scoped>
|
||||
@@ -129,4 +168,22 @@ useSortable(list, fields);
|
||||
Menlo,
|
||||
monospace;
|
||||
}
|
||||
|
||||
pre {
|
||||
:deep(.json-key) {
|
||||
@apply text-blue;
|
||||
}
|
||||
:deep(.json-string) {
|
||||
@apply text-green;
|
||||
}
|
||||
:deep(.json-number) {
|
||||
@apply text-orange;
|
||||
}
|
||||
:deep(.json-boolean) {
|
||||
@apply text-purple;
|
||||
}
|
||||
:deep(.json-null) {
|
||||
@apply text-red;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user