1
0
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:
Amir Raminfar
2024-09-04 08:53:40 -07:00
committed by GitHub
parent a62331a488
commit 9ddbe6fbb0
3 changed files with 63 additions and 7 deletions

View File

@@ -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']

View File

@@ -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">&lt;null&gt;</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>

View File

@@ -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>