From acb542462205b2bbaa34390fc18a0328755bd78d Mon Sep 17 00:00:00 2001
From: vthinkxie <yadong.xyd@alibaba-inc.com>
Date: Tue, 14 Apr 2020 17:24:29 +0800
Subject: [PATCH] [FLINK-17130][web] Enable listing JM logs and displaying logs
 by filename

This closes #11731.
---
 .../job-manager/job-manager-routing.module.ts | 16 ++++
 .../job-manager/job-manager.component.ts      |  3 +-
 .../pages/job-manager/job-manager.module.ts   |  4 +
 .../job-manager-log-detail.component.html     | 36 +++++++++
 .../job-manager-log-detail.component.less     | 54 +++++++++++++
 .../job-manager-log-detail.component.ts       | 78 +++++++++++++++++++
 .../job-manager-log-list.component.html       | 41 ++++++++++
 .../job-manager-log-list.component.ts         | 47 +++++++++++
 .../task-manager-log-detail.component.less    |  8 +-
 .../src/app/services/job-manager.service.ts   | 27 +++++++
 10 files changed, 312 insertions(+), 2 deletions(-)
 create mode 100644 flink-runtime-web/web-dashboard/src/app/pages/job-manager/log-detail/job-manager-log-detail.component.html
 create mode 100644 flink-runtime-web/web-dashboard/src/app/pages/job-manager/log-detail/job-manager-log-detail.component.less
 create mode 100644 flink-runtime-web/web-dashboard/src/app/pages/job-manager/log-detail/job-manager-log-detail.component.ts
 create mode 100644 flink-runtime-web/web-dashboard/src/app/pages/job-manager/log-list/job-manager-log-list.component.html
 create mode 100644 flink-runtime-web/web-dashboard/src/app/pages/job-manager/log-list/job-manager-log-list.component.ts

diff --git a/flink-runtime-web/web-dashboard/src/app/pages/job-manager/job-manager-routing.module.ts b/flink-runtime-web/web-dashboard/src/app/pages/job-manager/job-manager-routing.module.ts
index 0b4dbc1287fde..9ccaa2d8e74b4 100644
--- a/flink-runtime-web/web-dashboard/src/app/pages/job-manager/job-manager-routing.module.ts
+++ b/flink-runtime-web/web-dashboard/src/app/pages/job-manager/job-manager-routing.module.ts
@@ -20,6 +20,8 @@ import { NgModule } from '@angular/core';
 import { Routes, RouterModule } from '@angular/router';
 import { JobManagerConfigurationComponent } from './configuration/job-manager-configuration.component';
 import { JobManagerComponent } from './job-manager.component';
+import { JobManagerLogDetailComponent } from './log-detail/job-manager-log-detail.component';
+import { JobManagerLogListComponent } from './log-list/job-manager-log-list.component';
 import { JobManagerLogsComponent } from './logs/job-manager-logs.component';
 import { JobManagerStdoutComponent } from './stdout/job-manager-stdout.component';
 
@@ -49,6 +51,20 @@ const routes: Routes = [
           path: 'stdout'
         }
       },
+      {
+        path: 'log',
+        component: JobManagerLogListComponent,
+        data: {
+          path: 'log'
+        }
+      },
+      {
+        path: 'log/:logName',
+        component: JobManagerLogDetailComponent,
+        data: {
+          path: 'log'
+        }
+      },
       {
         path: '**',
         redirectTo: 'config',
diff --git a/flink-runtime-web/web-dashboard/src/app/pages/job-manager/job-manager.component.ts b/flink-runtime-web/web-dashboard/src/app/pages/job-manager/job-manager.component.ts
index ce14f28c9d39a..80951b31ff2c2 100644
--- a/flink-runtime-web/web-dashboard/src/app/pages/job-manager/job-manager.component.ts
+++ b/flink-runtime-web/web-dashboard/src/app/pages/job-manager/job-manager.component.ts
@@ -28,6 +28,7 @@ export class JobManagerComponent {
   listOfNavigation = [
     { path: 'config', title: 'Configuration' },
     { path: 'logs', title: 'Logs' },
-    { path: 'stdout', title: 'Stdout' }
+    { path: 'stdout', title: 'Stdout' },
+    { path: 'log', title: 'Log List' }
   ];
 }
diff --git a/flink-runtime-web/web-dashboard/src/app/pages/job-manager/job-manager.module.ts b/flink-runtime-web/web-dashboard/src/app/pages/job-manager/job-manager.module.ts
index 0c11c48b311c6..5d22c100fa8e1 100644
--- a/flink-runtime-web/web-dashboard/src/app/pages/job-manager/job-manager.module.ts
+++ b/flink-runtime-web/web-dashboard/src/app/pages/job-manager/job-manager.module.ts
@@ -23,6 +23,8 @@ import { ShareModule } from 'share/share.module';
 import { JobManagerRoutingModule } from './job-manager-routing.module';
 import { JobManagerComponent } from './job-manager.component';
 import { JobManagerConfigurationComponent } from './configuration/job-manager-configuration.component';
+import { JobManagerLogDetailComponent } from './log-detail/job-manager-log-detail.component';
+import { JobManagerLogListComponent } from './log-list/job-manager-log-list.component';
 import { JobManagerLogsComponent } from './logs/job-manager-logs.component';
 import { JobManagerStdoutComponent } from './stdout/job-manager-stdout.component';
 
@@ -31,6 +33,8 @@ import { JobManagerStdoutComponent } from './stdout/job-manager-stdout.component
   declarations: [
     JobManagerComponent,
     JobManagerConfigurationComponent,
+    JobManagerLogListComponent,
+    JobManagerLogDetailComponent,
     JobManagerLogsComponent,
     JobManagerStdoutComponent
   ]
diff --git a/flink-runtime-web/web-dashboard/src/app/pages/job-manager/log-detail/job-manager-log-detail.component.html b/flink-runtime-web/web-dashboard/src/app/pages/job-manager/log-detail/job-manager-log-detail.component.html
new file mode 100644
index 0000000000000..a627ebf15c978
--- /dev/null
+++ b/flink-runtime-web/web-dashboard/src/app/pages/job-manager/log-detail/job-manager-log-detail.component.html
@@ -0,0 +1,36 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<div class="breadcrumb">
+  <nz-breadcrumb>
+    <nz-breadcrumb-item>
+      <a [routerLink]="['../']"><i nz-icon type="rollback" theme="outline"></i> Log List</a>
+    </nz-breadcrumb-item>
+    <nz-breadcrumb-item>
+      {{ logName }}
+    </nz-breadcrumb-item>
+  </nz-breadcrumb>
+  <flink-refresh-download
+    [isLoading]="isLoading"
+    [downloadHref]="downloadUrl"
+    [downloadName]="logName"
+    (reload)="reload()"
+    (fullScreen)="toggleFullScreen($event)">
+  </flink-refresh-download>
+</div>
+<flink-monaco-editor [value]="logs"></flink-monaco-editor>
diff --git a/flink-runtime-web/web-dashboard/src/app/pages/job-manager/log-detail/job-manager-log-detail.component.less b/flink-runtime-web/web-dashboard/src/app/pages/job-manager/log-detail/job-manager-log-detail.component.less
new file mode 100644
index 0000000000000..3949a6f929ba0
--- /dev/null
+++ b/flink-runtime-web/web-dashboard/src/app/pages/job-manager/log-detail/job-manager-log-detail.component.less
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@import "theme";
+
+:host {
+  display: block;
+  height: 100%;
+  &.full-screen {
+    position: fixed;
+    top: 0;
+    bottom: 0;
+    right: 0;
+    left: 0;
+    background: @component-background;
+    z-index: 99;
+    flink-monaco-editor {
+      height: calc(~"100vh - 46px");
+    }
+  }
+}
+
+flink-monaco-editor {
+  height: calc(~"100vh - 205px");
+}
+
+.breadcrumb {
+  background: @component-background;
+  border-bottom: 1px solid @border-color-split;
+  padding: 12px 24px;
+  position: relative;
+}
+
+flink-refresh-download {
+  position: absolute;
+  right: 12px;
+  top: 0;
+  line-height: 47px;
+}
diff --git a/flink-runtime-web/web-dashboard/src/app/pages/job-manager/log-detail/job-manager-log-detail.component.ts b/flink-runtime-web/web-dashboard/src/app/pages/job-manager/log-detail/job-manager-log-detail.component.ts
new file mode 100644
index 0000000000000..b10ad6a0ab0c9
--- /dev/null
+++ b/flink-runtime-web/web-dashboard/src/app/pages/job-manager/log-detail/job-manager-log-detail.component.ts
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, ViewChild } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import { finalize } from 'rxjs/operators';
+import { JobManagerService } from 'services';
+import { MonacoEditorComponent } from 'share/common/monaco-editor/monaco-editor.component';
+
+@Component({
+  selector: 'flink-job-manager-log-detail',
+  templateUrl: './job-manager-log-detail.component.html',
+  changeDetection: ChangeDetectionStrategy.OnPush,
+  host: {
+    '[class.full-screen]': 'isFullScreen'
+  },
+  styleUrls: ['./job-manager-log-detail.component.less']
+})
+export class JobManagerLogDetailComponent implements OnInit {
+  logs = '';
+  logName = '';
+  downloadUrl = '';
+  isLoading = false;
+  isFullScreen = false;
+  @ViewChild(MonacoEditorComponent) monacoEditorComponent: MonacoEditorComponent;
+  constructor(
+    private jobManagerService: JobManagerService,
+    private cdr: ChangeDetectorRef,
+    private activatedRoute: ActivatedRoute
+  ) {}
+
+  reload() {
+    this.isLoading = true;
+    this.cdr.markForCheck();
+    this.jobManagerService
+      .loadLog(this.logName)
+      .pipe(
+        finalize(() => {
+          this.isLoading = false;
+          this.layoutEditor();
+          this.cdr.markForCheck();
+        })
+      )
+      .subscribe(data => {
+        this.logs = data.data;
+        this.downloadUrl = data.url;
+      });
+  }
+
+  layoutEditor(): void {
+    setTimeout(() => this.monacoEditorComponent.layout());
+  }
+
+  toggleFullScreen(fullScreen: boolean) {
+    this.isFullScreen = fullScreen;
+    this.layoutEditor();
+  }
+
+  ngOnInit() {
+    this.logName = this.activatedRoute.snapshot.params.logName;
+    this.reload();
+  }
+}
diff --git a/flink-runtime-web/web-dashboard/src/app/pages/job-manager/log-list/job-manager-log-list.component.html b/flink-runtime-web/web-dashboard/src/app/pages/job-manager/log-list/job-manager-log-list.component.html
new file mode 100644
index 0000000000000..efe09b34af559
--- /dev/null
+++ b/flink-runtime-web/web-dashboard/src/app/pages/job-manager/log-list/job-manager-log-list.component.html
@@ -0,0 +1,41 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<nz-card [nzBordered]="false">
+  <nz-table
+    [nzSize]="'small'"
+    [nzData]="listOfLog"
+    [nzLoading]="isLoading"
+    [nzFrontPagination]="false"
+    [nzShowPagination]="false">
+    <thead>
+      <tr>
+        <th>Log Name</th>
+        <th>Size (KB)</th>
+      </tr>
+    </thead>
+    <tbody>
+      <tr *ngFor="let log of listOfLog;">
+        <td>
+          <a [routerLink]="[log.name]">{{ log.name }}</a>
+        </td>
+        <td>{{ (log.size / 1024) | number : '1.0-2' }}</td>
+      </tr>
+    </tbody>
+  </nz-table>
+</nz-card>
diff --git a/flink-runtime-web/web-dashboard/src/app/pages/job-manager/log-list/job-manager-log-list.component.ts b/flink-runtime-web/web-dashboard/src/app/pages/job-manager/log-list/job-manager-log-list.component.ts
new file mode 100644
index 0000000000000..9068b7bb98213
--- /dev/null
+++ b/flink-runtime-web/web-dashboard/src/app/pages/job-manager/log-list/job-manager-log-list.component.ts
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
+import { finalize } from 'rxjs/operators';
+import { JobManagerService } from 'services';
+
+@Component({
+  selector: 'flink-job-manager-log-list',
+  templateUrl: './job-manager-log-list.component.html',
+  changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class JobManagerLogListComponent implements OnInit {
+  listOfLog: { name: string; size: number }[] = [];
+  isLoading = true;
+
+  constructor(private jobManagerService: JobManagerService, private cdr: ChangeDetectorRef) {}
+
+  ngOnInit() {
+    this.jobManagerService
+      .loadLogList()
+      .pipe(
+        finalize(() => {
+          this.isLoading = false;
+          this.cdr.markForCheck();
+        })
+      )
+      .subscribe(data => {
+        this.listOfLog = data;
+      });
+  }
+}
diff --git a/flink-runtime-web/web-dashboard/src/app/pages/task-manager/log-detail/task-manager-log-detail.component.less b/flink-runtime-web/web-dashboard/src/app/pages/task-manager/log-detail/task-manager-log-detail.component.less
index d9bd69a31ff1b..73d1d21b1465a 100644
--- a/flink-runtime-web/web-dashboard/src/app/pages/task-manager/log-detail/task-manager-log-detail.component.less
+++ b/flink-runtime-web/web-dashboard/src/app/pages/task-manager/log-detail/task-manager-log-detail.component.less
@@ -27,8 +27,14 @@
     left: 0;
     background: @component-background;
     z-index: 99;
+    .breadcrumb {
+      margin-bottom: 0px;
+      border-top: 0px;
+    }
     flink-monaco-editor {
-      height: calc(~"100vh - 65px");
+      border-top: 0px;
+      border-bottom: 0px;
+      height: calc(~"100vh - 46px");
     }
   }
 }
diff --git a/flink-runtime-web/web-dashboard/src/app/services/job-manager.service.ts b/flink-runtime-web/web-dashboard/src/app/services/job-manager.service.ts
index 06b6aa2ea5856..aa83524a82aac 100644
--- a/flink-runtime-web/web-dashboard/src/app/services/job-manager.service.ts
+++ b/flink-runtime-web/web-dashboard/src/app/services/job-manager.service.ts
@@ -19,6 +19,7 @@
 import { HttpClient, HttpHeaders } from '@angular/common/http';
 import { Injectable } from '@angular/core';
 import { BASE_URL } from 'config';
+import { map } from 'rxjs/operators';
 
 @Injectable({
   providedIn: 'root'
@@ -50,6 +51,32 @@ export class JobManagerService {
       headers: new HttpHeaders().append('Cache-Control', 'no-cache')
     });
   }
+  /**
+   * Load JM log list
+   */
+  loadLogList() {
+    return this.httpClient
+      .get<{ logs: Array<{ name: string; size: number }> }>(`${BASE_URL}/jobmanager/logs`)
+      .pipe(map(data => data.logs));
+  }
+
+  /**
+   * Load JM log
+   * @param logName
+   */
+  loadLog(logName: string) {
+    const url = `${BASE_URL}/jobmanager/logs/${logName}`;
+    return this.httpClient
+      .get(url, { responseType: 'text', headers: new HttpHeaders().append('Cache-Control', 'no-cache') })
+      .pipe(
+        map(data => {
+          return {
+            data,
+            url
+          };
+        })
+      );
+  }
 
   constructor(private httpClient: HttpClient) {}
 }