jcloud/dashboard/src2/components/site/SiteAnalytics.vue
2025-04-12 17:39:38 +08:00

482 lines
13 KiB
Vue

<template>
<div class="space-y-4 p-5">
<div class="flex space-x-4">
<FormControl
class="ml-auto w-32"
type="select"
:options="durationOptions"
v-model="duration"
/>
</div>
<ErrorMessage
:message="
$resources.analytics.error || $resources.advancedAnalytics.error
"
/>
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
<AnalyticsCard title="每日使用量">
<LineChart
type="time"
title="使用计数器"
:key="usageCounterData"
:data="usageCounterData"
unit="seconds"
:chartTheme="[$theme.colors.purple[500]]"
:loading="$resources.analytics.loading"
:showCard="false"
class="h-[15.55rem] p-2 pb-3"
/>
</AnalyticsCard>
<AnalyticsCard title="运行时间">
<SiteUptime
:data="$resources.analytics?.data?.uptime"
:loading="$resources.analytics.loading"
class="h-[15.55rem] p-2 pb-3"
/>
</AnalyticsCard>
<AnalyticsCard title="请求">
<LineChart
type="time"
title="请求"
:key="requestCountData"
:data="requestCountData"
unit="requests"
:chartTheme="[$theme.colors.green[500]]"
:loading="$resources.analytics.loading"
:showCard="false"
class="h-[15.55rem] p-2 pb-3"
/>
<template #action>
<router-link
class="text-base text-gray-600 hover:text-gray-700"
:to="{ name: 'Site Performance Request Logs' }"
>
请求日志报告
</router-link>
</template>
</AnalyticsCard>
<AnalyticsCard title="请求 CPU 使用率">
<LineChart
type="time"
title="请求 CPU 使用率"
:key="requestTimeData"
:data="requestTimeData"
unit="seconds"
:chartTheme="[$theme.colors.yellow[500]]"
:loading="$resources.analytics.loading"
:showCard="false"
class="h-[15.55rem] p-2 pb-3"
/>
</AnalyticsCard>
</div>
<div
class="!mt-6 flex w-fit cursor-pointer space-x-2"
@click="toggleAdvancedAnalytics"
>
<h2 class="text-lg font-semibold">高级分析</h2>
<FeatherIcon
class="h-5 w-5 text-gray-500 hover:text-gray-700"
:name="showAdvancedAnalytics ? 'chevron-down' : 'chevron-right'"
/>
</div>
<!-- 高级分析 -->
<div
v-if="showAdvancedAnalytics"
class="grid grid-cols-1 gap-5 sm:grid-cols-2"
>
<AnalyticsCard title="后台任务">
<LineChart
type="time"
title="后台任务"
:key="jobCountData"
:data="jobCountData"
unit="jobs"
:chartTheme="[$theme.colors.red[500]]"
:loading="$resources.advancedAnalytics.loading"
:showCard="false"
class="h-[15.55rem] p-2 pb-3"
/>
</AnalyticsCard>
<AnalyticsCard title="后台任务 CPU 使用率">
<LineChart
type="time"
title="后台任务 CPU 使用率"
:key="jobTimeData"
:data="jobTimeData"
unit="seconds"
:chartTheme="[$theme.colors.blue[500]]"
:loading="$resources.advancedAnalytics.loading"
:showCard="false"
class="h-[15.55rem] p-2 pb-3"
/>
</AnalyticsCard>
<AnalyticsCard class="sm:col-span-2" title="频繁请求">
<BarChart
title="按路径的请求计数"
:key="requestCountByPathData"
:data="requestCountByPathData"
unit="requests"
:chartTheme="requestChartColors"
:loading="$resources.advancedAnalytics.loading"
:showCard="false"
class="h-[15.55rem] p-2 pb-3"
/>
</AnalyticsCard>
<AnalyticsCard class="sm:col-span-2" title="最慢请求">
<BarChart
:key="requestDurationByPathData"
:data="requestDurationByPathData"
unit="秒"
:chartTheme="requestChartColors"
:loading="$resources.advancedAnalytics.loading"
:showCard="false"
class="h-[15.55rem] p-2 pb-3"
/>
</AnalyticsCard>
<AnalyticsCard
class="sm:col-span-2"
title="单个请求时间(平均)"
>
<BarChart
:key="averageRequestDurationByPathData"
:data="averageRequestDurationByPathData"
unit="秒"
:chartTheme="requestChartColors"
:loading="$resources.advancedAnalytics.loading"
:showCard="false"
class="h-[15.55rem] p-2 pb-3"
/>
</AnalyticsCard>
<AnalyticsCard class="sm:col-span-2" title="频繁后台任务">
<BarChart
:key="backgroundJobCountByMethodData"
:data="backgroundJobCountByMethodData"
unit="任务"
:chartTheme="requestChartColors"
:loading="$resources.advancedAnalytics.loading"
:showCard="false"
class="h-[15.55rem] p-2 pb-3"
/>
</AnalyticsCard>
<AnalyticsCard class="sm:col-span-2" title="最慢后台任务">
<BarChart
:key="backgroundJobDurationByMethodData"
:data="backgroundJobDurationByMethodData"
unit="秒"
:chartTheme="requestChartColors"
:loading="$resources.advancedAnalytics.loading"
:showCard="false"
class="h-[15.55rem] p-2 pb-3"
/>
</AnalyticsCard>
<AnalyticsCard
class="sm:col-span-2"
title="单个后台任务时间(平均)"
>
<BarChart
:key="averageBackgroundJobDurationByMethodData"
:data="averageBackgroundJobDurationByMethodData"
unit="秒"
:chartTheme="requestChartColors"
:loading="$resources.advancedAnalytics.loading"
:showCard="false"
class="h-[15.55rem] p-2 pb-3"
/>
</AnalyticsCard>
<AnalyticsCard class="sm:col-span-2" title="频繁慢查询">
<template #action>
<Tooltip text="显示详细报告">
<router-link
class="ml-2 mr-auto text-base text-gray-600 hover:text-gray-700"
:to="{ name: 'Site Performance Slow Queries' }"
>
</router-link>
</Tooltip>
<TabButtons
:buttons="[{ label: '非规范化' }, { label: '规范化' }]"
v-model="slowLogsFrequencyType"
/>
</template>
<BarChart
:key="slowLogsCountData"
:data="slowLogsCountData"
unit="查询"
:chartTheme="requestChartColors"
:loading="$resources.advancedAnalytics.loading"
:showCard="false"
class="h-[15.55rem] p-2 pb-3"
/>
</AnalyticsCard>
<AnalyticsCard class="sm:col-span-2" title="最慢查询">
<template #action>
<Tooltip text="显示详细报告">
<router-link
class="ml-2 mr-auto text-base text-gray-600 hover:text-gray-700"
:to="{ name: 'Site Performance Slow Queries' }"
>
</router-link>
</Tooltip>
<TabButtons
:buttons="[{ label: '非规范化' }, { label: '规范化' }]"
v-model="slowLogsDurationType"
/>
</template>
<BarChart
:key="slowLogsDurationData"
:data="slowLogsDurationData"
unit="秒"
:chartTheme="requestChartColors"
:loading="$resources.advancedAnalytics.loading"
:showCard="false"
class="h-[15.55rem] p-2 pb-3"
/>
</AnalyticsCard>
</div>
</div>
</template>
<script>
import dayjs from '../../utils/dayjs';
import LineChart from '@/components/charts/LineChart.vue';
import BarChart from '@/components/charts/BarChart.vue';
import SiteUptime from './SiteUptime.vue';
import AlertBanner from '../AlertBanner.vue';
import AnalyticsCard from './AnalyticsCard.vue';
export default {
name: 'SiteAnalytics',
props: ['name'],
components: {
BarChart,
LineChart,
SiteUptime,
AlertBanner,
AnalyticsCard,
},
data() {
return {
duration: '24h',
showAdvancedAnalytics: false,
localTimezone: dayjs.tz.guess(),
slowLogsDurationType: 'Denormalized',
slowLogsFrequencyType: 'Denormalized',
durationOptions: [
{ label: '时长', value: null, disabled: true },
{ label: '1小时', value: '1h' },
{ label: '6小时', value: '6h' },
{ label: '24小时', value: '24h' },
{ label: '7天', value: '7d' },
{ label: '15天', value: '15d' },
],
};
},
resources: {
analytics() {
return {
url: 'jcloud.api.analytics.get',
params: {
name: this.name,
timezone: this.localTimezone,
duration: this.duration,
},
auto: true,
};
},
advancedAnalytics() {
return {
url: 'jcloud.api.analytics.get_advanced_analytics',
params: {
name: this.name,
timezone: this.localTimezone,
duration: this.duration,
},
auto: this.showAdvancedAnalytics,
};
},
slowLogsCount() {
return {
url: 'jcloud.api.analytics.get_slow_logs_by_query',
params: {
name: this.name,
agg_type: 'count',
timezone: this.localTimezone,
duration: this.duration,
normalize: this.slowLogsFrequencyType === 'Normalized',
},
auto: this.showAdvancedAnalytics,
};
},
slowLogsDuration() {
return {
url: 'jcloud.api.analytics.get_slow_logs_by_query',
params: {
name: this.name,
agg_type: 'duration',
timezone: this.localTimezone,
duration: this.duration,
normalize: this.slowLogsDurationType === 'Normalized',
},
auto: this.showAdvancedAnalytics,
};
},
},
computed: {
requestChartColors() {
return [
this.$theme.colors.green[500],
this.$theme.colors.red[500],
this.$theme.colors.yellow[500],
this.$theme.colors.pink[500],
this.$theme.colors.purple[500],
this.$theme.colors.blue[500],
this.$theme.colors.teal[500],
this.$theme.colors.cyan[500],
this.$theme.colors.gray[500],
this.$theme.colors.orange[500],
];
},
usageCounterData() {
let data = this.$resources.analytics.data?.usage_counter;
if (!data) return;
let plan_limit = this.$resources.analytics.data?.plan_limit;
return {
datasets: [data.map((d) => [+new Date(d.date), d.value / 1000000])],
// 每日限制标记
markLine: {
data: [
{
name: '每日计算限制',
yAxis: plan_limit,
label: {
formatter: '{b}: {c} 秒',
position: 'middle',
},
lineStyle: {
color: '#f5222d',
},
},
],
symbol: ['none', 'none'],
},
};
},
requestCountData() {
let requestCount = this.$resources.analytics.data?.request_count;
if (!requestCount) return;
return {
datasets: [requestCount.map((d) => [+new Date(d.date), d.value])],
};
},
requestCountByPathData() {
let requestCountByPath =
this.$resources.advancedAnalytics.data?.request_count_by_path;
if (!requestCountByPath) return;
return requestCountByPath;
},
requestDurationByPathData() {
let requestDurationByPath =
this.$resources.advancedAnalytics.data?.request_duration_by_path;
if (!requestDurationByPath) return;
return requestDurationByPath;
},
averageRequestDurationByPathData() {
let averageRequestDurationByPath =
this.$resources.advancedAnalytics.data
?.average_request_duration_by_path;
if (!averageRequestDurationByPath) return;
return averageRequestDurationByPath;
},
backgroundJobCountByMethodData() {
let backgroundJobCountByMethod =
this.$resources.advancedAnalytics.data?.background_job_count_by_method;
if (!backgroundJobCountByMethod) return;
return backgroundJobCountByMethod;
},
backgroundJobDurationByMethodData() {
let backgroundJobDurationByMethod =
this.$resources.advancedAnalytics.data
?.background_job_duration_by_method;
if (!backgroundJobDurationByMethod) return;
return backgroundJobDurationByMethod;
},
averageBackgroundJobDurationByMethodData() {
let averageBackgroundJobDurationByMethod =
this.$resources.advancedAnalytics.data
?.average_background_job_duration_by_method;
if (!averageBackgroundJobDurationByMethod) return;
return averageBackgroundJobDurationByMethod;
},
slowLogsDurationData() {
const slowLogs = this.$resources.slowLogsDuration.data;
if (!slowLogs) return;
return slowLogs;
},
slowLogsCountData() {
const slowLogs = this.$resources.slowLogsCount.data;
if (!slowLogs) return;
return slowLogs;
},
requestTimeData() {
let requestCpuTime = this.$resources.analytics.data?.request_cpu_time;
if (!requestCpuTime) return;
return {
datasets: [
requestCpuTime.map((d) => [+new Date(d.date), d.value / 1000000]),
],
};
},
jobCountData() {
let jobCount = this.$resources.advancedAnalytics.data?.job_count;
if (!jobCount) return;
return {
datasets: [jobCount.map((d) => [+new Date(d.date), d.value])],
};
},
jobTimeData() {
let jobCpuTime = this.$resources.advancedAnalytics.data?.job_cpu_time;
if (!jobCpuTime) return;
return {
datasets: [
jobCpuTime.map((d) => [+new Date(d.date), d.value / 1000000]),
],
};
},
},
methods: {
toggleAdvancedAnalytics() {
this.showAdvancedAnalytics = !this.showAdvancedAnalytics;
},
},
};
</script>