-
Notifications
You must be signed in to change notification settings - Fork 31
Expand file tree
/
Copy pathLogDetailsModal.vue
More file actions
217 lines (192 loc) Β· 5.58 KB
/
LogDetailsModal.vue
File metadata and controls
217 lines (192 loc) Β· 5.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
<!--
SPDX-FileCopyrightText: 2023 Nextcloud Gmbh and Nextcloud contributors
SPDX-License-Identifier: AGPL-3.0-or-later
-->
<template>
<NcModal
:show="open"
size="large"
:has-previous="index > 0"
:has-next="index < logEntries.length - 1"
@next="$emit('update:currentEntry', logEntries[index + 1])"
@previous="$emit('update:currentEntry', logEntries[index - 1])"
@update:show="$emit('update:open', false)">
<template #default>
<div class="log-details">
<dl :class="cssLevelClass">
<dt>{{ t('logreader', 'Level') }}</dt>
<dd>{{ levelString }}</dd>
<dt>{{ t('logreader', 'App') }}</dt>
<dd>
{{ currentEntry?.app || t('logreader', 'No app in context') }}
</dd>
<dt>{{ t('logreader', 'Time') }}</dt>
<dd>{{ timeString }}</dd>
</dl>
<div class="log-details__actions">
<NcButton :aria-label="t('logreader', 'Copy raw entry')" variant="tertiary" @click="copyRaw">
<template #icon>
<IconContentCopy />
</template>
{{ t('logreader', 'Copy raw entry') }}
</NcButton>
<NcButton :aria-label="t('logreader', 'Copy formatted entry')" variant="tertiary" @click="copyFormatted">
<template #icon>
<IconContentCopy />
</template>
{{ t('logreader', 'Copy formatted entry') }}
</NcButton>
<NcButton
v-if="currentEntry.exception"
class="log-details__btn"
@click="isExceptionExpanded = !isExceptionExpanded">
{{ isExceptionExpanded ? t('logreader', 'Hide exception details') : t('logreader', 'View exception details') }}
</NcButton>
</div>
<template v-if="currentEntry.exception">
<LogException :exception="currentEntry.exception" class="log-details__exception" :is-expanded="isExceptionExpanded" />
<hr>
</template>
<figure class="log-details__raw">
<figcaption>{{ t('logreader', 'Raw log entry') }}</figcaption>
<!-- eslint-disable-next-line vue/no-v-html -->
<pre><code class="hljs language-json" v-html="code" /></pre>
</figure>
</div>
</template>
</NcModal>
</template>
<script setup lang="ts">
import type { ILogEntry } from '../interfaces'
import { showSuccess } from '@nextcloud/dialogs'
import { translate as t } from '@nextcloud/l10n'
import hljs from 'highlight.js/lib/core'
import json from 'highlight.js/lib/languages/json'
import { computed, ref, watchEffect } from 'vue'
import NcButton from '@nextcloud/vue/components/NcButton'
import NcModal from '@nextcloud/vue/components/NcModal'
import IconContentCopy from 'vue-material-design-icons/ContentCopy.vue'
import LogException from './exception/LogException.vue'
import { LOGGING_LEVEL, LOGGING_LEVEL_NAMES } from '../constants'
import { copyToCipboard } from '../utils/clipboard'
import { useLogFormatting } from '../utils/format'
import 'highlight.js/styles/base16/material-darker.css'
const props = defineProps<{
open: boolean
currentEntry: ILogEntry
logEntries: readonly ILogEntry[]
}>()
hljs.registerLanguage('json', json)
const { formatTime, formatLogEntry } = useLogFormatting()
/**
* Whether to show the full exception
*/
const isExceptionExpanded = ref(!!props.currentEntry.exception)
/**
* Hide exception is current entry is changes
*/
watchEffect(() => {
isExceptionExpanded.value = !!props.currentEntry.exception
})
/**
* Index of the current entry within all entries
*/
const index = computed(() => props.logEntries.findIndex((entry) => entry === props.currentEntry))
/**
* Formatted data of the entry
*/
const code = computed(() => hljs.highlight(JSON.stringify(props.currentEntry, null, 2), { language: 'json' }).value)
/**
* Level as translated human readable string
*/
const levelString = computed(() => LOGGING_LEVEL_NAMES[props.currentEntry.level])
/**
* Time as localized string
*/
const timeString = computed(() => formatTime(props.currentEntry.time))
/**
* css classes to assign logging level as border color
*/
const cssLevelClass = computed(() => [
'log-details__info',
`log-details__info--${LOGGING_LEVEL[props.currentEntry.level]}`,
])
/**
* Copy the raw log entry as json
*/
async function copyRaw() {
if (await copyToCipboard(JSON.stringify(props.currentEntry))) {
showSuccess(t('logreader', 'Log entry successfully copied'))
}
}
/**
* Copy the log entry formatted to be human readable
*/
async function copyFormatted() {
if (await copyToCipboard(formatLogEntry(props.currentEntry))) {
showSuccess(t('logreader', 'Log entry successfully copied'))
}
}
</script>
<style lang="scss" scoped>
.log-details {
padding: 12px;
&__raw, &__exception {
padding-block-start: 12px;
}
&__info {
display: flex;
justify-content: space-between;
border-block-end: 4px solid;
padding-inline-end: 50px;
padding-block: 13px 4px;
margin-block-end: 13px;
dt, dd {
padding: 0;
}
dt {
font-weight: bold;
&::after {
content: ':';
}
}
&--debug {
border-block-end-color: var(--color-border-maxcontrast);
}
&--info {
border-block-end-color: var(--color-element-info, var(--color-info));
}
&--warning {
border-block-end-color: var(--color-element-warning, var(--color-warning));
}
&--error, &--fatal {
border-block-end-color: var(--color-element-error, var(--color-error));
}
}
&__actions {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: end;
gap: 9px;
margin-block: 9px;
}
hr {
color: var(--color-border-dark);
}
}
.hljs {
border-radius: var(--border-radius-large);
}
// For mobile we need to show the details as a vertical list
@media only screen and (max-width: 399px) {
.log-details {
&__info {
display: block;
}
dd {
margin-inline-start: 12px;
}
}
}
</style>