index.vue 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982
  1. <template>
  2. <BasicLayout>
  3. <template #wrapper>
  4. <el-card class="box-card" style="flex:1;">
  5. <div style="margin-bottom:16px;">
  6. <!-- <i class="el-icon-arrow-left" style="font-size:24px;cursor: pointer;vertical-align: middle;" @click='back'></i> -->
  7. <!-- <el-button type="primary" icon="el-icon-refresh-left" size="mini" @click='back' plain>返回</el-button> -->
  8. <el-page-header @back="back">
  9. <div slot="content">
  10. <span style="font-size:18px;">{{ cnName }}【 {{ spansQuery.service_name }} 】</span>
  11. </div>
  12. </el-page-header>
  13. </div>
  14. <div v-if="activeName==&quot;first&quot;" class="block-container" style="margin-bottom:20px">
  15. <div class="row">
  16. <div class="col">
  17. <el-tooltip placement="bottom" effect="dark" :visible-arrow="false">
  18. <div slot="content">
  19. <i class="dot" />
  20. <span>{{ digitsObj.Span }}</span>
  21. </div>
  22. <div class="border-bottom">
  23. <div class="block-box px-3 py-4">
  24. <div class="col-tit">SPANS/min</div>
  25. <div class="col-con">{{ digitsObj.Span !=undefined?digitsObj.Span:0 }}</div>
  26. </div>
  27. </div>
  28. </el-tooltip>
  29. </div>
  30. <div class="col">
  31. <el-tooltip placement="bottom" effect="dark" :visible-arrow="false">
  32. <div slot="content">
  33. <i class="dot" />
  34. <span>{{ digitsObj.Http }}</span>
  35. </div>
  36. <div class="border-bottom">
  37. <div class="block-box px-3 py-4">
  38. <div class="col-tit">HTTP/min</div>
  39. <div class="col-con">{{ digitsObj.Http !=undefined?digitsObj.Http:0 }}</div>
  40. </div>
  41. </div>
  42. </el-tooltip>
  43. </div>
  44. <div class="col">
  45. <el-tooltip placement="bottom" effect="dark" :visible-arrow="false">
  46. <div slot="content">
  47. <i class="dot" />
  48. <span>{{ digitsObj.Rpc }}</span>
  49. </div>
  50. <div class="border-bottom">
  51. <div class="block-box px-3 py-4">
  52. <div class="col-tit">RPC/min</div>
  53. <div class="col-con">{{ digitsObj.Rpc !=undefined?(digitsObj.Rpc).toFixed(2):0 }}</div>
  54. </div>
  55. </div>
  56. </el-tooltip>
  57. </div>
  58. <!-- <div class="col">
  59. <el-tooltip placement="bottom" effect="dark" :visible-arrow='false'>
  60. <div slot="content">
  61. <i class="dot"></i>
  62. <span>{{digitsObj.error_rate}}</span>
  63. </div>
  64. <div class="border-bottom">
  65. <div class="block-box px-3 py-4">
  66. <div class="col-tit">LOGS</div>
  67. <div class="col-con">{{(digitsObj.error_rate*100).toFixed(2)}}/min</div>
  68. </div>
  69. </div>
  70. </el-tooltip>
  71. </div> -->
  72. <div class="col">
  73. <el-tooltip placement="bottom" effect="dark" :visible-arrow="false">
  74. <div slot="content">
  75. <i class="dot" />
  76. <span>{{ digitsObj.Error!=undefined?digitsObj.Error:0 }}</span>
  77. </div>
  78. <div class="border-bottom">
  79. <div class="block-box px-3 py-4">
  80. <div class="col-tit">HTTPERROR/min</div>
  81. <div class="col-con">{{ digitsObj.Error!=undefined?(digitsObj.Error).toFixed(2):0 }}</div>
  82. </div>
  83. </div>
  84. </el-tooltip>
  85. </div>
  86. <div class="col">
  87. <el-tooltip placement="bottom" effect="dark" :visible-arrow="false">
  88. <div slot="content">
  89. <i class="dot" />
  90. <span>{{ digitsObj.DB }}</span>
  91. </div>
  92. <div class="border-bottom">
  93. <div class="block-box px-3 py-4">
  94. <div class="col-tit">DATABASE/min</div>
  95. <div class="col-con">{{ digitsObj.DB !=undefined?digitsObj.DB :0 }}</div>
  96. </div>
  97. </div>
  98. </el-tooltip>
  99. </div>
  100. </div>
  101. </div>
  102. <div v-if="activeName==&quot;second&quot;">
  103. <div id="scaleMain" style="width:100%;height:240px;margin-bottom:36px;margin-top:16px" />
  104. </div>
  105. <div class="link-box">
  106. <router-link class="to-inline-block" :to="{path:'/service/Interface/index'}">
  107. <el-button type="primary" size="mini">接口解析</el-button>
  108. </router-link>
  109. <router-link class="to-inline-block" :to="{path:'/service/process/index'}">
  110. <el-button type="primary" size="mini">进程解析</el-button>
  111. </router-link>
  112. </div>
  113. <el-tabs v-model="activeName" @tab-click="handleClick">
  114. <el-tab-pane label="对比分析" name="first">
  115. <el-form ref="timeQuery" :model="timeQuery" :inline="true" label-width="100px" style="text-align: right;">
  116. <el-form-item label="时间跨度" prop="hour_num">
  117. <el-select v-model="timeQuery.hour_num" placeholder="时间选择" clearable size="small" @keyup.enter.native="handletimeQuery" @change="changetimeQuery">
  118. <el-option
  119. v-for="time in timeOptions"
  120. :key="time.value"
  121. :label="time.label"
  122. :value="time.value"
  123. />
  124. </el-select>
  125. </el-form-item>
  126. </el-form>
  127. <div>
  128. <h6 class="h6">活跃度</h6>
  129. <!-- <div class="echartsWrap" v-if="ServiceCompareObj.current.length>0"> -->
  130. <div class="echartsWrap">
  131. <div id="serviceActive" style="width:100vw;height:220px" />
  132. </div>
  133. <!-- <div v-else class="noData">
  134. <div style="text-align: center;color: rgba(36, 41, 46, 0.75);font-size: 18px; width: 100%;line-height:200px">No Data</div>
  135. </div> -->
  136. <h6 class="h6">错误率分布</h6>
  137. <!-- <div class="echartsWrap" v-if="errorRateObj.current.length>0"> -->
  138. <div class="echartsWrap">
  139. <div id="errorRate" style="width:100vw;height:220px" />
  140. </div>
  141. <!-- <div v-else class="noData">
  142. <div style="text-align: center;color: rgba(36, 41, 46, 0.75);font-size: 18px; width: 100%;line-height:200px">No Data</div>
  143. </div> -->
  144. </div>
  145. </el-tab-pane>
  146. <el-tab-pane label="Span列表" name="second">
  147. <div style="display:flex">
  148. <el-form ref="spansQuery" :model="spansQuery" :inline="true" label-width="100px">
  149. <el-form-item label="自定义属性" prop="span_attribute_key">
  150. <el-input
  151. v-model="spansQuery.span_attribute_key"
  152. placeholder="请输入自定义属性"
  153. clearable
  154. size="small"
  155. @keyup.enter.native="handleSpansQuery"
  156. />
  157. </el-form-item>
  158. <el-form-item label="自定义属性值" prop="span_attribute_value">
  159. <el-input
  160. v-model="spansQuery.span_attribute_value"
  161. placeholder="请输入自定义属性值"
  162. clearable
  163. size="small"
  164. @keyup.enter.native="handleSpansQuery"
  165. />
  166. </el-form-item>
  167. <el-form-item label="Span类型" prop="span_kind">
  168. <el-select v-model="spansQuery.span_kind" placeholder="Span类型" clearable size="small" @keyup.enter.native="handleSpansQuery" @change="changeSpansQuery">
  169. <el-option
  170. v-for="kind in spanOptions"
  171. :key="kind.value"
  172. :label="kind.label"
  173. :value="kind.value"
  174. />
  175. </el-select>
  176. </el-form-item>
  177. <el-form-item label="延迟大于" prop="min_duration">
  178. <el-input
  179. v-model="spansQuery.min_duration"
  180. placeholder=""
  181. clearable
  182. size="small"
  183. type="number"
  184. class="Dur_input"
  185. @keyup.enter.native="handleSpansQuery"
  186. >
  187. <template slot="append">ms</template>
  188. </el-input>
  189. </el-form-item>
  190. <el-form-item label="调用方法" prop="request_method">
  191. <el-input
  192. v-model="spansQuery.request_method"
  193. placeholder="请输入调用方法"
  194. clearable
  195. size="small"
  196. @keyup.enter.native="handleSpansQuery"
  197. />
  198. </el-form-item>
  199. <!-- <el-form-item label="最大延迟" prop="max_duration">
  200. <el-input
  201. v-model="spansQuery.max_duration"
  202. placeholder="请输入最大延迟"
  203. clearable
  204. size="small"
  205. type="number"
  206. @keyup.enter.native="handleSpansQuery"
  207. />
  208. </el-form-item> -->
  209. <el-form-item label="" prop="only_exception">
  210. <el-checkbox v-model="spansQuery.only_exception" :true-label="1" :false-label="0" @keyup.enter.native="handleSpansQuery" @change="changeErrorSpans">仅异常</el-checkbox>
  211. </el-form-item>
  212. <el-form-item label="" prop="only_database">
  213. <!-- <el-checkbox v-model="spansQuery.only_database"></el-checkbox> -->
  214. <el-checkbox v-model="spansQuery.only_database" :true-label="1" :false-label="0" @keyup.enter.native="handleSpansQuery" @change="changeSQLSpans">仅SQL</el-checkbox>
  215. </el-form-item>
  216. </el-form>
  217. <div style="display:inline-block;vertical-align: middle;">
  218. <el-dropdown
  219. style="margin-left: 10px"
  220. size="mini"
  221. trigger="click"
  222. placement="bottom"
  223. >
  224. <el-button type="primary" size="mini">
  225. <!-- 下拉菜单 -->
  226. <i class="el-icon-s-grid" />
  227. <!-- 配置项 -->
  228. <i class="el-icon-arrow-down" />
  229. </el-button>
  230. <el-dropdown-menu slot="dropdown">
  231. <el-checkbox-group
  232. v-model="colSelect"
  233. style="text-align: left"
  234. @change="columnChange"
  235. >
  236. <el-dropdown-item
  237. v-for="(item, index) in colData"
  238. :key="index"
  239. ><el-checkbox :label="item.title">{{
  240. item.title
  241. }}</el-checkbox></el-dropdown-item>
  242. </el-checkbox-group>
  243. </el-dropdown-menu>
  244. </el-dropdown>
  245. </div>
  246. </div>
  247. <div style="padding-bottom:16px;">
  248. <el-table
  249. v-loading="loading"
  250. :data="spansList"
  251. @sort-change="sortChangeTable"
  252. >
  253. <!-- <el-table-column type="selection" width="45" align="center" /> -->
  254. <el-table-column v-if="isShow('TraceId')" label="TraceId" prop="trace_id" :show-overflow-tooltip="true">
  255. <template slot-scope="scope">
  256. <span @click="goto(scope.row)">{{ scope.row.trace_id }}</span>
  257. </template>
  258. </el-table-column>
  259. <el-table-column v-if="isShow('ServiceName')" label="ServiceName" prop="service_name" :show-overflow-tooltip="true" />
  260. <el-table-column v-if="isShow('DateTime')" label="DateTime" sortable="custom" prop="datetime" :show-overflow-tooltip="true" />
  261. <el-table-column v-if="isShow('Method')" label="Method" prop="method" :show-overflow-tooltip="true" />
  262. <el-table-column v-if="isShow('Code')" label="Code" prop="code" :show-overflow-tooltip="true" />
  263. <el-table-column v-if="isShow('Duration(ms)')" label="Duration(ms)" prop="duration" :show-overflow-tooltip="true">
  264. <template slot-scope="scope">
  265. <el-tag v-if="scope.row.duration>=2000" type="danger">{{ scope.row.duration }}</el-tag>
  266. <el-tag v-if="scope.row.duration<2000" type="info">{{ scope.row.duration }}</el-tag>
  267. </template>
  268. </el-table-column>
  269. <el-table-column
  270. v-if="isShow('SpanId')"
  271. label="SpanId"
  272. prop="span_id"
  273. :show-overflow-tooltip="true"
  274. />
  275. </el-table>
  276. <pagination
  277. v-show="total>0"
  278. :total="total"
  279. :page.sync="spansQuery.pageIndex"
  280. :limit.sync="spansQuery.pageSize"
  281. @pagination="getServiceSpans"
  282. />
  283. </div>
  284. </el-tab-pane>
  285. </el-tabs>
  286. </el-card>
  287. </template>
  288. </BasicLayout>
  289. </template>
  290. <script>
  291. import { digitsService, serviceSpans, serviceCompare, serviceErrors, listServiceStats } from '@/api/service'
  292. import storage from '@/utils/storage'
  293. import moment from 'moment'
  294. export default {
  295. data() {
  296. return {
  297. timeQuery: {
  298. app_alias: '',
  299. service_name: '',
  300. hour_num: 1
  301. },
  302. timeOptions: [
  303. {
  304. value: 1,
  305. label: '1 小时'
  306. },
  307. {
  308. value: 3,
  309. label: '3 小时'
  310. },
  311. {
  312. value: 6,
  313. label: '6 小时'
  314. },
  315. {
  316. value: 12,
  317. label: '12 小时'
  318. },
  319. {
  320. value: 24,
  321. label: '24 小时'
  322. }
  323. ],
  324. spanOptions: [
  325. {
  326. value: 'SPAN_KIND_CLIENT',
  327. label: 'SPAN_KIND_CLIENT'
  328. },
  329. {
  330. value: 'SPAN_KIND_SERVER',
  331. label: 'SPAN_KIND_SERVER'
  332. },
  333. {
  334. value: 'SPAN_KIND_INTERNAL',
  335. label: 'SPAN_KIND_INTERNAL'
  336. },
  337. {
  338. value: 'SPAN_KIND_CONSUMER',
  339. label: 'SPAN_KIND_CONSUMER'
  340. },
  341. {
  342. value: 'SPAN_KIND_PRODUCER',
  343. label: 'SPAN_KIND_PRODUCER'
  344. }
  345. ],
  346. // 设置选中的列的复选框
  347. colSelect: [
  348. 'TraceId',
  349. 'ServiceName',
  350. 'DateTime',
  351. 'Method',
  352. 'Code',
  353. 'Duration(ms)'
  354. ],
  355. colData: [
  356. { title: 'TraceId', istrue: false },
  357. { title: 'ServiceName', istrue: false },
  358. { title: 'DateTime', istrue: false },
  359. { title: 'Method', istrue: false },
  360. { title: 'Code', istrue: false },
  361. { title: 'Duration(ms)', istrue: false },
  362. { title: 'SpanId', istrue: false }
  363. ],
  364. loading: false,
  365. total: 0,
  366. activeName: 'first',
  367. digitsObj: {},
  368. digitsQuery: {
  369. app_alias: '',
  370. service_name: ''
  371. },
  372. timer: null,
  373. spansQuery: {
  374. app_alias: '',
  375. request_method: '',
  376. service_name: '', // 支持多个,多个并列
  377. only_exception: 0, // 仅显示异常trace相关
  378. only_database: 0, // 仅显示数据库相关
  379. // 支持分页,参数与之前一致
  380. // 支持时间筛选,参数与之前一致
  381. trace_id: '', // trace id, 字符串
  382. span_kind: '', // 固定5个值,建议下拉列表,分别为SPAN_KIND_CLIENT SPAN_KIND_SERVER SPAN_KIND_INTERNAL SPAN_KIND_CONSUMER SPAN_KIND_PRODUCER
  383. span_attribute_key: '', // 字符串
  384. span_attribute_value: '', // 字符串
  385. min_duration: 0, // 最小耗时
  386. max_duration: 0, // 最大耗时
  387. start_time: 0,
  388. end_time: 0,
  389. pageIndex: 1,
  390. pageSize: 10,
  391. sort_field: 'Timestamp',
  392. sort_type: 'DESC'
  393. },
  394. spansList: [],
  395. ServiceCompareObj: {
  396. current: [],
  397. forward: [],
  398. time: []
  399. },
  400. errorRateObj: {
  401. current: [],
  402. forward: [],
  403. time: []
  404. },
  405. compareChart: null,
  406. errorChart: null,
  407. cnName: '',
  408. statsQuery: {
  409. id: 0,
  410. start_time: '',
  411. end_time: ''
  412. },
  413. statsObj: {}
  414. }
  415. },
  416. watch: {
  417. '$store.state.time.globalTimes': {
  418. handler(newValue, oldValue) {
  419. if (newValue) {
  420. this.digitsQuery.start_time = newValue.startTime
  421. this.digitsQuery.end_time = newValue.endTime
  422. this.statsQuery.start_time = newValue.startTime
  423. this.statsQuery.end_time = newValue.endTime
  424. this.spansQuery.start_time = newValue.startTime
  425. this.spansQuery.end_time = newValue.endTime
  426. this.getDigitsService()
  427. this.getlistServiceStats()
  428. this.getServiceSpans()
  429. setTimeout(() => {
  430. this.drawEchartsScale(this.statsObj)
  431. }, 300)
  432. if (newValue.timeOut) {
  433. if (newValue.timeOut == 1) {
  434. clearInterval(this.timer)
  435. } else {
  436. clearInterval(this.timer)
  437. this.Refresh(newValue.timeOut)
  438. }
  439. }
  440. }
  441. },
  442. // immediate: true,
  443. deep: true
  444. }
  445. },
  446. created() {
  447. // 说明
  448. // appItem中的id和app_id是一样的
  449. this.appItem = storage.get('appsItem')
  450. const start_time = this.$store.state.time.globalTimes.startTime
  451. const end_time = this.$store.state.time.globalTimes.endTime
  452. if (JSON.stringify(this.appItem) != '{}') {
  453. this.digitsQuery.app_alias = this.appItem.alias
  454. this.digitsQuery.app_id = this.appItem.id
  455. this.digitsQuery.start_time = start_time
  456. this.digitsQuery.end_time = end_time
  457. this.spansQuery.app_alias = this.appItem.alias
  458. this.spansQuery.start_time = start_time
  459. this.spansQuery.end_time = end_time
  460. this.timeQuery.app_alias = this.appItem.alias
  461. this.statsQuery.start_time = start_time
  462. this.statsQuery.end_time = end_time
  463. }
  464. // 从业务解析中节点中的所属服务跳转过来的和业务解析列表中每列跳转过来的,服务名
  465. if (this.$route.query.service_name != undefined) {
  466. this.digitsQuery.service_name = this.$route.query.service_name
  467. this.spansQuery.service_name = this.$route.query.service_name
  468. this.timeQuery.service_name = this.$route.query.service_name
  469. this.statsQuery.id = this.$route.query.id
  470. this.getDigitsService()
  471. this.getServiceSpans()
  472. this.getServiceCompare()
  473. this.getErrorRateCompare()
  474. this.getlistServiceStats()
  475. }
  476. if (this.$route.query.name != undefined) {
  477. this.cnName = this.$route.query.name
  478. }
  479. const strr = window.location.href
  480. const param = this.parseQueryString(strr)
  481. if (param.app_alias != undefined && param.service_name != undefined) {
  482. this.digitsQuery.app_alias = param.app_alias
  483. this.spansQuery.app_alias = param.app_alias
  484. this.timeQuery.app_alias = param.app_alias
  485. this.digitsQuery.service_name = param.service_name
  486. this.spansQuery.service_name = param.service_name
  487. this.timeQuery.service_name = param.service_name
  488. this.getDigitsService()
  489. this.getServiceSpans()
  490. this.getServiceCompare()
  491. this.getErrorRateCompare()
  492. }
  493. // this.getDigitsService();
  494. // this.getServiceSpans();
  495. // this.getServiceCompare();
  496. // this.getErrorRateCompare();
  497. // this.getlistServiceStats();
  498. this.columnChange()
  499. },
  500. mounted() {
  501. this.$nextTick(() => {
  502. // this.initServiceCompareChart(this.ServiceCompareObj)
  503. // this.initErrorRateCompareChart(this.errorRateObj)
  504. // this.$echarts5.connect([this.errorChart, this.compareChart]);
  505. })
  506. },
  507. beforeDestroy() {
  508. clearInterval(this.timer)
  509. },
  510. methods: {
  511. Refresh(timeOut) {
  512. this.timer = setInterval(() => {
  513. this.getDigitsService()
  514. this.getlistServiceStats()
  515. this.getServiceSpans()
  516. this.getServiceCompare()
  517. this.getErrorRateCompare()
  518. }, timeOut)
  519. },
  520. getlistServiceStats() {
  521. listServiceStats(this.statsQuery).then(response => {
  522. if (response.code == 200) {
  523. this.statsObj = response.data
  524. }
  525. })
  526. },
  527. drawEchartsScale(row) {
  528. const _this = this
  529. const myChartScale = this.$echarts5.init(document.getElementById('scaleMain'))
  530. // 绘制趋势echarts
  531. const option = {
  532. tooltip: {
  533. trigger: 'axis',
  534. // confine: false,
  535. // appendToBody: true,
  536. show: true,
  537. formatter: function(params) {
  538. storage.set('paramsValue', params)
  539. const axisValueLabel = params[0].axisValueLabel
  540. let str0 = ''
  541. params.forEach((item, idx) => {
  542. // str1+=`${}`
  543. str0 += `${item.marker}${item.seriesName}<span style='margin-left:30px;text-align:right;font-weight:400'>${item.data}</span>`
  544. switch (idx) {
  545. case 0:
  546. str0
  547. break
  548. case 1:
  549. str0
  550. break
  551. default:
  552. str0
  553. }
  554. str0 += idx === params.length - 1 ? '' : '<br/>'
  555. })
  556. return axisValueLabel + '<br>' + str0
  557. }
  558. },
  559. title: {
  560. text: '延迟比例',
  561. // subtext: 'ms',
  562. textStyle: {
  563. fontSize: 14
  564. }
  565. // triggerEvent: true,
  566. },
  567. grid: {
  568. // top:'5%',
  569. left: '3%',
  570. right: '6%',
  571. bottom: '1%',
  572. containLabel: true
  573. },
  574. xAxis: {
  575. type: 'category',
  576. boundaryGap: false,
  577. data: row.quantiles.time,
  578. axisLabel: {
  579. textStyle: {
  580. fontSize: 12,
  581. textAlign: 'center'
  582. },
  583. formatter: function(params) {
  584. // let newParams = moment(params).format('YYYY-MM-DD HH:mm:ss');
  585. // let newArr = newParams.split(' ')
  586. // let time = newArr[0] + "\n" + newArr[1]
  587. // return time;
  588. let newParams = ''
  589. const dateStr = params.substring(0, 10)
  590. // dateStr = dateStr.replace(/\-/g, function(match) {
  591. // return match.replace(/\-/g, '.');
  592. // });
  593. const timeStr = params.substring(11, 19)
  594. newParams = dateStr + '\n' + timeStr
  595. return newParams
  596. }
  597. }
  598. },
  599. yAxis: {
  600. type: 'value',
  601. scale: true,
  602. gridIndex: 0,
  603. axisLabel: {
  604. formatter: '{value}'
  605. },
  606. axisLine: {
  607. show: true
  608. },
  609. axisTick: {
  610. show: true
  611. },
  612. splitLine: {
  613. show: true
  614. }
  615. },
  616. series: [
  617. {
  618. name: 'P.50',
  619. type: 'line',
  620. stack: 'Total',
  621. symbol: 'none',
  622. // symbol: 'emptyCircle',//拐点
  623. data: row.quantiles.p50
  624. },
  625. {
  626. name: 'P.95',
  627. type: 'line',
  628. stack: 'Total',
  629. symbol: 'none', // 拐点
  630. // symbol: 'emptyCircle',//拐点
  631. data: row.quantiles.p90
  632. },
  633. {
  634. name: 'P.99',
  635. type: 'line',
  636. stack: 'Total',
  637. symbol: 'none', // 拐点
  638. // symbol: 'emptyCircle',//拐点
  639. data: row.quantiles.p99
  640. }
  641. ]
  642. }
  643. myChartScale.setOption(option)
  644. myChartScale.getZr().off('click')
  645. myChartScale.getZr().on('click', params => {
  646. const data = storage.get('paramsValue')
  647. const timestamp = moment(data[0].name).unix()
  648. _this.spansQuery.start_time = timestamp
  649. _this.spansQuery.end_time = timestamp
  650. _this.getServiceSpans()
  651. })
  652. },
  653. parseQueryString(url) {
  654. var json = {}
  655. var arr = url.substr(url.indexOf('?') + 1).split('&')
  656. arr.forEach(item => {
  657. var tmp = item.split('=')
  658. json[tmp[0]] = decodeURIComponent(tmp[1])
  659. })
  660. return json
  661. },
  662. handletimeQuery() {
  663. this.getServiceCompare()
  664. this.getErrorRateCompare()
  665. },
  666. changetimeQuery(val) {
  667. this.timeQuery.hour_num = val
  668. this.getServiceCompare()
  669. this.getErrorRateCompare()
  670. },
  671. handleSpansQuery() {
  672. this.getServiceSpans()
  673. },
  674. changeErrorSpans(val) {
  675. this.spansQuery.only_exception = val
  676. this.getServiceSpans()
  677. },
  678. changeSQLSpans(val) {
  679. this.spansQuery.only_database = val
  680. this.getServiceSpans()
  681. },
  682. changeSpansQuery(val) {
  683. this.spansQuery.span_kind = val
  684. this.getServiceSpans()
  685. },
  686. // 控制列的显示和隐藏
  687. columnChange(val) {
  688. this.colData.filter((i) => {
  689. if (this.colSelect.indexOf(i.title) !== -1) {
  690. i.istrue = true
  691. } else {
  692. i.istrue = false
  693. }
  694. })
  695. },
  696. handleClick(tab, event) {
  697. if (tab.name == 'second') {
  698. this.$nextTick(() => {
  699. this.drawEchartsScale(this.statsObj)
  700. })
  701. }
  702. },
  703. getDigitsService() {
  704. digitsService(this.digitsQuery).then((res) => {
  705. if (res.code == 200) {
  706. this.digitsObj = res.data
  707. }
  708. })
  709. },
  710. getServiceSpans() {
  711. serviceSpans(this.spansQuery).then((res) => {
  712. if (res.code == 200) {
  713. this.spansList = res.data.list
  714. this.total = res.data.count
  715. }
  716. })
  717. },
  718. getServiceCompare() {
  719. serviceCompare(this.timeQuery).then(res => {
  720. if (res.code == 200) {
  721. this.ServiceCompareObj = res.data
  722. // if(this.ServiceCompareObj.current.length>0){
  723. this.initServiceCompareChart(this.ServiceCompareObj)
  724. // }
  725. }
  726. })
  727. },
  728. initServiceCompareChart(data) {
  729. const _this = this
  730. this.compareChart = this.$echarts5.init(document.getElementById('serviceActive'))
  731. this.compareChart.setOption({
  732. tooltip: {
  733. trigger: 'axis',
  734. axisPointer: {
  735. type: 'cross',
  736. crossStyle: {
  737. color: '#999'
  738. }
  739. }
  740. },
  741. legend: {
  742. data: ['当前时段', '前续时段']
  743. // selected: {
  744. // '当前时段': true,
  745. // '前续时段对比': false
  746. // }
  747. },
  748. // toolbox: {
  749. // show:true,
  750. // feature: {
  751. // // dataView: { show: true, readOnly: false },
  752. // magicType: { show: true, type: ['line', 'bar'] },
  753. // restore: { show: true },
  754. // // saveAsImage: { show: true }
  755. // }
  756. // },
  757. grid: {
  758. // top:'5%',
  759. left: '3%',
  760. right: '16%',
  761. bottom: '1%',
  762. containLabel: true
  763. },
  764. xAxis: [
  765. {
  766. type: 'category',
  767. // type:'time',
  768. data: data.time
  769. // axisPointer: {
  770. // type: 'shadow'
  771. // },
  772. }
  773. ],
  774. yAxis: [
  775. {
  776. type: 'value'
  777. }
  778. ],
  779. series: [
  780. {
  781. name: '当前时段',
  782. type: 'bar',
  783. tooltip: {
  784. valueFormatter: function(value) {
  785. return value
  786. }
  787. },
  788. barWidth: 8,
  789. data: data.current != undefined ? data.current : []
  790. },
  791. {
  792. type: 'line',
  793. name: '前续时段',
  794. tooltip: {
  795. valueFormatter: function(value) {
  796. return value
  797. }
  798. },
  799. lineStyle: {
  800. width: 3
  801. },
  802. data: data.forward != undefined ? data.forward : []
  803. }
  804. ]
  805. })
  806. this.compareChart.group = 'group1'
  807. this.$echarts5.connect('group1')
  808. },
  809. getErrorRateCompare() {
  810. serviceErrors(this.timeQuery).then(res => {
  811. if (res.code == 200) {
  812. this.errorRateObj = res.data
  813. this.initErrorRateCompareChart(this.errorRateObj)
  814. }
  815. })
  816. },
  817. initErrorRateCompareChart(data) {
  818. this.errorChart = this.$echarts5.init(document.getElementById('errorRate'))
  819. this.errorChart.setOption({
  820. tooltip: {
  821. trigger: 'axis',
  822. axisPointer: {
  823. type: 'cross',
  824. crossStyle: {
  825. color: '#999'
  826. }
  827. }
  828. },
  829. legend: {
  830. data: ['当前时段', '前续时段']
  831. // selected: {
  832. // '当前时段': true,
  833. // '前续时段对比': false
  834. // }
  835. },
  836. // toolbox: {
  837. // show:true,
  838. // feature: {
  839. // // dataView: { show: true, readOnly: false },
  840. // magicType: { show: true, type: ['line', 'bar'] },
  841. // restore: { show: true },
  842. // // saveAsImage: { show: true }
  843. // }
  844. // },
  845. grid: {
  846. // top:'5%',
  847. left: '3%',
  848. right: '16%',
  849. bottom: '1%',
  850. containLabel: true
  851. },
  852. xAxis: [
  853. {
  854. type: 'category',
  855. data: data.time
  856. }
  857. ],
  858. yAxis: [
  859. {
  860. type: 'value'
  861. }
  862. ],
  863. series: [
  864. {
  865. name: '当前时段',
  866. type: 'bar',
  867. tooltip: {
  868. valueFormatter: function(value) {
  869. return value
  870. }
  871. },
  872. barWidth: 8,
  873. data: data.current != undefined ? data.current : []
  874. },
  875. {
  876. type: 'line',
  877. name: '前续时段',
  878. tooltip: {
  879. valueFormatter: function(value) {
  880. return value
  881. }
  882. },
  883. lineStyle: {
  884. width: 3
  885. },
  886. data: data.forward != undefined ? data.forward : []
  887. }
  888. ]
  889. })
  890. this.errorChart.group = 'group1'
  891. this.$echarts5.connect('group1')
  892. },
  893. back() {
  894. this.$router.push({
  895. path: '/service/service/index'
  896. })
  897. },
  898. goto(row) {
  899. const datetime = Date.parse(row.datetime) / 1000
  900. const href = this.$router.resolve({
  901. path: '/latency/index',
  902. query: {
  903. id: row.trace_id,
  904. span_id: row.span_id,
  905. datetime: datetime
  906. }
  907. })
  908. window.open(window.location.origin + '/' + href.href, '_blank')
  909. },
  910. isShow(label) {
  911. return this.colData.filter(i => i.title === label)[0].istrue
  912. },
  913. sortChangeTable(val) {
  914. // desc(倒序)和asc(正序)
  915. if (val.order === 'descending') {
  916. this.spansQuery.sort_type = 'DESC'
  917. } else if (val.order === 'ascending') {
  918. this.spansQuery.sort_type = 'ASC'
  919. }
  920. this.getServiceSpans()
  921. }
  922. }
  923. }
  924. </script>
  925. <style lang="scss" scoped>
  926. .echartsWrap{
  927. width:100%;
  928. }
  929. .h6{
  930. font-size: 14px;
  931. }
  932. .Dur_input ::v-deep .el-input__inner{
  933. width:100px;
  934. }
  935. .noData{
  936. width: 100%;
  937. height: 200px;
  938. background: #fff;
  939. }
  940. .noData_title{
  941. margin-bottom: 0px;
  942. padding: 2px 5px;
  943. text-overflow: ellipsis;
  944. overflow: hidden;
  945. white-space: nowrap;
  946. font-size: 14px;
  947. font-weight: 500;
  948. line-height: 1.57143;
  949. font-family: Inter, Helvetica, Arial, sans-serif;
  950. }
  951. .link-box{
  952. display:none;
  953. text-align: right;
  954. // position:relative;
  955. // top:26px;
  956. .to-inline-block{
  957. display: inline-block;
  958. + .to-inline-block{
  959. margin-left:10px;
  960. }
  961. button{
  962. font-size:14px;
  963. }
  964. }
  965. }
  966. </style>