Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ JAVA_OPTS_FOR_JDK_17="-Dfile.encoding=UTF-8 -Xmx4096m -DlogPath=$LOG_DIR/jni.log
# https://github.com/apache/doris/blob/master/docs/zh-CN/community/developer-guide/debug-tool.md#jemalloc-heap-profile
# https://jemalloc.net/jemalloc.3.html
JEMALLOC_CONF="percpu_arena:percpu,background_thread:true,metadata_thp:auto,muzzy_decay_ms:5000,dirty_decay_ms:5000,oversize_threshold:0,prof:false,lg_prof_interval:-1"
JEMALLOC_PROF_PRFIX="jemalloc_heap_profile_"
JEMALLOC_PROF_PREFIX="jemalloc_heap_profile_"

# ports for admin, web, heartbeat service
be_port = ${be_port}
Expand Down
6 changes: 5 additions & 1 deletion bigtop-manager-ui/src/api/metrics/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
*/

import { get } from '@/api/request-util'
import type { MetricsData, TimeRangeType } from './types'
import type { MetricsData, ServiceMetrics, TimeRangeType } from './types'

export const getClusterMetricsInfo = (paramsPath: { id: number }, params: { interval: TimeRangeType }) => {
return get<MetricsData>(`/metrics/clusters/${paramsPath.id}`, params)
Expand All @@ -27,3 +27,7 @@ export const getClusterMetricsInfo = (paramsPath: { id: number }, params: { inte
export const getHostMetricsInfo = (paramsPath: { id: number }, params: { interval: TimeRangeType }) => {
return get<MetricsData>(`/metrics/hosts/${paramsPath.id}`, params)
}

export const getServiceMetricsInfo = (paramsPath: { id: number }, params: { interval: TimeRangeType }) => {
return get<ServiceMetrics>(`/metrics/services/${paramsPath.id}`, params)
}
27 changes: 27 additions & 0 deletions bigtop-manager-ui/src/api/metrics/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/

export type TimeRangeType = '1m' | '5m' | '15m' | '30m' | '1h' | '2h'

export type MetricsData = {
cpuUsageCur: string
memoryUsageCur: string
Expand All @@ -34,3 +35,29 @@ export type MetricsData = {
diskWrite: string[]
timestamps: string[]
}

export type ServiceMetricType =
| 'NUMBER' // Numeric value, unitless
| 'PERCENT' // Percent, followed by %
| 'BYTE' // Byte, the front end can convert it through formatFromByte in storage util, the unit is B/KB/MB, etc.
| 'MILLISECOND' // milliseconds, followed by ms
| 'BPS' // Bytes per second, followed by B/s
| 'NPS' // Numeric value per second, followed by N/s

type ServiceMetricItemSeries = {
name: string
type: string
data: any[]
[propName: string]: any
}

export type ServiceMetricItem = {
title: string
valueType: Lowercase<ServiceMetricType>
series: ServiceMetricItemSeries[]
}

export type ServiceMetrics = {
charts: ServiceMetricItem[]
timestamps: string[]
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@
for (const service of selectedServices.value) {
if (!service.name || map.has(service.name)) continue

const requireds = extractRequireds(service.configs)
if (requireds.length > 0) map.set(service.name, requireds)
const requires = extractRequires(service.configs)
if (requires.length > 0) map.set(service.name, requires)
}

return map
Expand Down Expand Up @@ -168,10 +168,7 @@
const matchKeyword = (keyword: string, prop: Property, config?: ServiceConfig) => {
const lowerKeyword = keyword.toLowerCase()
const includesProp =
prop.name?.toLowerCase().includes(lowerKeyword) ||
prop.value?.toLowerCase().includes(lowerKeyword) ||
prop.displayName?.toLowerCase().includes(lowerKeyword)

prop.name?.toLowerCase().includes(lowerKeyword) || prop.value?.toLowerCase().includes(lowerKeyword)
if (config != undefined) {
return config.name?.toLowerCase().includes(lowerKeyword) || includesProp
}
Expand All @@ -186,13 +183,13 @@
* @param configs - The service configurations to check.
* @returns A list of configurations with their required properties.
*/
const extractRequireds = (configs?: ServiceConfig[]) => {
const extractRequires = (configs?: ServiceConfig[]) => {
const result: ServiceConfig[] = []

for (const config of configs ?? []) {
const requireds = config.properties?.filter((p) => p.attrs?.required && p.value == '') ?? []
if (requireds.length > 0) {
result.push({ [config.name!]: requireds })
const requires = config.properties?.filter((p) => p.attrs?.required && p.value == '') ?? []
if (requires.length > 0) {
result.push({ [config.name!]: requires })
}
}

Expand Down Expand Up @@ -299,12 +296,12 @@
/>
<div
v-else
:title="property.displayName ?? property.name"
:title="property.name"
class="property-name"
:class="{ 'required-mark': property.attrs?.required }"
>
<span>
{{ property.displayName ?? property.name }}
{{ property.name }}
</span>
</div>
</a-form-item>
Expand Down
25 changes: 13 additions & 12 deletions bigtop-manager-ui/src/features/metric/category-chart.vue
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,10 @@
* @param legendMap - An array of [key, displayName] pairs.
* @returns An array of ECharts series config objects with populated and formatted data.
*/
const generateChartSeries = <T,>(data: Partial<T>, legendMap: [string, string][]) => {
const generateChartSeries = <T,>(data: Partial<T>, legendMap: [string, string][], config = baseConfig) => {
return legendMap.map(([key, name]) => ({
name,
...baseConfig,
...config,
data: (data[key] || []).map((v: unknown) => roundFixed(v))
}))
}
Expand All @@ -164,24 +164,25 @@

watchEffect(() => {
let series = [] as any,
legend = [] as any
legend = { data: [] } as any

const { series: temp_series = [] } = data.value
const xAxis = xAxisData.value?.map((v) => dayjs(Number(v) * 1000).format('HH:mm')) ?? []

if (legendMap.value) {
legend = new Map(legendMap.value).values()
series = generateChartSeries(data.value, legendMap.value)
} else {
series = [
{
name: title.value.toLowerCase(),
data: data.value.map((v) => roundFixed(v))
}
]
if (temp_series.length > 1) {
legend.data = temp_series.map((s) => s.name)
series = [...temp_series]
} else {
series = [{ name: title.value.toLowerCase(), data: data.value.map((v) => roundFixed(v)) }]
}
}

setOptions({
xAxis: xAxisData.value
? [{ data: xAxisData.value?.map((v) => dayjs(Number(v) * 1000).format('HH:mm')) || [] }]
: [],
xAxis: [{ data: xAxis ?? xAxis }],
...config.value,
legend,
series
Expand Down
9 changes: 3 additions & 6 deletions bigtop-manager-ui/src/features/service-management/configs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,7 @@
const matchKeyword = (keyword: string, prop: Property, config?: ServiceConfig) => {
const lowerKeyword = keyword.toLowerCase()
const includesProp =
prop.name?.toLowerCase().includes(lowerKeyword) ||
prop.value?.toLowerCase().includes(lowerKeyword) ||
prop.displayName?.toLowerCase().includes(lowerKeyword)

prop.name?.toLowerCase().includes(lowerKeyword) || prop.value?.toLowerCase().includes(lowerKeyword)
if (config != undefined) {
return config.name?.toLowerCase().includes(lowerKeyword) || includesProp
}
Expand Down Expand Up @@ -233,12 +230,12 @@
/>
<div
v-else
:title="property.displayName ?? property.name"
:title="property.name"
class="property-name"
:class="{ 'required-mark': property.attrs?.required }"
>
<span>
{{ property.displayName ?? property.name }}
{{ property.name }}
</span>
</div>
</a-form-item>
Expand Down
5 changes: 4 additions & 1 deletion bigtop-manager-ui/src/features/service-management/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,10 @@
<main-card v-model:active-key="activeKey" :tabs="tabs">
<template #tab-item>
<keep-alive>
<component :is="getCompName" v-bind="{ ...serviceDetail, clusterId: routeParams.id }"></component>
<component
:is="getCompName"
v-bind="{ ...serviceDetail, clusterId: routeParams.id, ...routeParams }"
></component>
</keep-alive>
</template>
</main-card>
Expand Down
139 changes: 71 additions & 68 deletions bigtop-manager-ui/src/features/service-management/overview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,76 +19,88 @@

<script setup lang="ts">
import { CommonStatus, CommonStatusTexts } from '@/enums/state'
import { Empty } from 'ant-design-vue'
import { formatFromByte } from '@/utils/storage.ts'
import { getServiceMetricsInfo } from '@/api/metrics'
import { isEmpty } from '@/utils/tools'

import GaugeChart from '@/features/metric/gauge-chart.vue'
import CategoryChart from '@/features/metric/category-chart.vue'

import { Empty } from 'ant-design-vue'
import type { ServiceVO, ServiceStatusType } from '@/api/service/types'

type TimeRangeText = '1m' | '15m' | '30m' | '1h' | '6h' | '30h'
type TimeRangeItem = {
text: TimeRangeText
time: string
}
import type { ServiceMetricItem, ServiceMetrics, ServiceMetricType, TimeRangeType } from '@/api/metrics/types'

const { t } = useI18n()
const attrs = useAttrs() as unknown as Required<ServiceVO> & { clusterId: number }
const currTimeRange = ref<TimeRangeText>('15m')
const chartData = ref({
chart1: [],
chart2: [],
chart3: [],
chart4: []
})
const attrs = useAttrs() as any
const currTimeRange = ref<TimeRangeType>('5m')
const chartData = ref<Partial<ServiceMetrics>>({})

const timeRanges = shallowRef<TimeRangeType[]>(['1m', '5m', '15m', '30m', '1h', '2h'])
const statusColors = shallowRef<Record<ServiceStatusType, keyof typeof CommonStatusTexts>>({
1: 'healthy',
2: 'unhealthy',
3: 'unknown'
})

const unitMap: Record<Lowercase<ServiceMetricType>, string | ((value: number) => string)> = {
number: '',
percent: '%',
byte: (val) => formatFromByte(val, 0),
millisecond: 'ms',
bps: 'B/s',
nps: 'N/s'
}

const clusterId = computed(() => attrs.id)
const noChartData = computed(() => Object.values(chartData.value).length === 0)
const serviceKeys = computed(() => Object.keys(baseConfig.value) as (keyof ServiceVO)[])
const noChartData = computed(() => Object.values(chartData.value).every((v) => v.length === 0))
const timeRanges = computed((): TimeRangeItem[] => [
{
text: '1m',
time: ''
},
{
text: '15m',
time: ''
},
{
text: '30m',
time: ''
},
{
text: '1h',
time: ''
},
{
text: '6h',
time: ''
},
{
text: '30h',
time: ''
}
])
const baseConfig = computed((): Partial<Record<keyof ServiceVO, string>> => {
return {
const baseConfig = computed(
(): Partial<Record<keyof ServiceVO, string>> => ({
status: t('overview.service_status'),
displayName: t('overview.service_name'),
version: t('overview.service_version'),
stack: t('common.stack'),
restartFlag: t('service.required_restart'),
metrics: t('overview.metrics'),
kerberos: t('overview.kerberos')
})
)

const handleTimeRange = (time: TimeRangeType) => {
if (currTimeRange.value === time) return
currTimeRange.value = time
getServiceMetrics()
}

const getServiceMetrics = async () => {
try {
const res = await getServiceMetricsInfo({ id: attrs.serviceId }, { interval: currTimeRange.value })
chartData.value = { ...res }
} catch (error) {
console.log('Failed to fetch service metrics:', error)
}
})
}

const handleTimeRange = (time: TimeRangeItem) => {
currTimeRange.value = time.text
const getChartFormatter = (chart: ServiceMetricItem) => {
const unit = unitMap[chart.valueType]
const valueWithUnit = (val: any) => (typeof unit === 'function' ? unit(val as number) : `${val} ${unit}`)
return {
tooltip: (val: any) => `${isEmpty(val) ? '--' : valueWithUnit(val)}`,
yAxis: valueWithUnit
}
}

const { pause, resume } = useIntervalFn(getServiceMetrics, 30000, { immediate: true })

onActivated(() => {
if (clusterId.value == 0) return
getServiceMetrics()
resume()
})

onDeactivated(() => {
if (clusterId.value == 0) return
pause()
})
</script>

<template>
Expand Down Expand Up @@ -155,13 +167,13 @@
<a-space :size="12">
<div
v-for="time in timeRanges"
:key="time.text"
:key="time"
tabindex="0"
class="time-range"
:class="{ 'time-range-activated': currTimeRange === time.text }"
:class="{ 'time-range-activated': currTimeRange === time }"
@click="handleTimeRange(time)"
>
{{ time.text }}
{{ time }}
</div>
</a-space>
</div>
Expand All @@ -171,24 +183,15 @@
</div>
</template>
<a-row v-else class="box-content">
<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
<div class="chart-item-wrp">
<gauge-chart chart-id="chart1" :title="t('overview.memory_usage')" />
</div>
</a-col>
<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
<div class="chart-item-wrp">
<gauge-chart chart-id="chart2" :title="t('overview.cpu_usage')" />
</div>
</a-col>
<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
<div class="chart-item-wrp">
<category-chart chart-id="chart4" :title="t('overview.cpu_usage')" />
</div>
</a-col>
<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
<a-col v-for="(chart, index) in chartData.charts" :key="index" :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
<div class="chart-item-wrp">
<category-chart chart-id="chart3" :title="t('overview.memory_usage')" />
<category-chart
:chart-id="`chart${index}`"
:x-axis-data="chartData?.timestamps"
:data="chart"
:title="chart.title"
:formatter="getChartFormatter(chart)"
/>
</div>
</a-col>
</a-row>
Expand Down
Loading
Loading