LogStore.ts 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. /*
  2. Copyright (C) 2019 Cloudbase Solutions SRL
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. */
  14. import { observable, runInAction, action } from "mobx";
  15. import cookie from "js-cookie";
  16. import JSZip from "jszip";
  17. import { saveAs } from "file-saver";
  18. import type { Log } from "@src/@types/Log";
  19. import configLoader from "@src/utils/Config";
  20. import apiCaller from "@src/utils/ApiCaller";
  21. import DateUtils from "@src/utils/DateUtils";
  22. import DomUtils from "@src/utils/DomUtils";
  23. import ObjectUtils from "@src/utils/ObjectUtils";
  24. import notificationStore from "./NotificationStore";
  25. const MAX_STREAM_LINES = 200;
  26. const generateUrlForLog = (
  27. logName: string,
  28. startDate?: Date | null,
  29. endDate?: Date | null
  30. ): string => {
  31. const token = cookie.get("token") || "null";
  32. let url = `${configLoader.config.servicesUrls.coriolisLogs}/${logName}?auth_type=keystone&auth_token=${token}`;
  33. if (startDate) {
  34. url += `&start_date=${DateUtils.toUnix(startDate)}`;
  35. }
  36. if (endDate) {
  37. url += `&end_date=${DateUtils.toUnix(endDate)}`;
  38. }
  39. return url;
  40. };
  41. const downloadDiagnosticsIntoZip = async (zipRef: JSZip): Promise<void> => {
  42. const baseUrl = `${configLoader.config.servicesUrls.coriolis}/${apiCaller.projectId}`;
  43. const [diagnosticsResp, transfersResp, deploymentsResp] = await Promise.all([
  44. apiCaller.send({ url: `${baseUrl}/diagnostics` }),
  45. apiCaller.send({ url: `${baseUrl}/transfers?show_deleted=true` }),
  46. apiCaller.send({ url: `${baseUrl}/deployments?show_deleted=true` }),
  47. ]);
  48. zipRef.file("diagnostics.json", JSON.stringify(diagnosticsResp.data));
  49. zipRef.file("transfers.json", JSON.stringify(transfersResp.data));
  50. zipRef.file("deployments.json", JSON.stringify(deploymentsResp.data));
  51. };
  52. class LogStore {
  53. @observable logs: Log[] = [];
  54. @observable loading = false;
  55. @observable liveFeed: string[] = [];
  56. @observable generatingDiagnostics = false;
  57. @observable downloadingAllLogs = false;
  58. @action async getLogs(options?: { showLoading?: boolean }) {
  59. if (options && options.showLoading) {
  60. this.loading = true;
  61. }
  62. try {
  63. const response = await apiCaller.send({
  64. url: configLoader.config.servicesUrls.coriolisLogs,
  65. });
  66. runInAction(() => {
  67. this.logs = response.data.logs;
  68. this.loading = false;
  69. });
  70. } finally {
  71. runInAction(() => {
  72. this.loading = false;
  73. });
  74. }
  75. }
  76. @action async downloadAll(startDate?: Date | null, endDate?: Date | null) {
  77. this.downloadingAllLogs = true;
  78. await ObjectUtils.waitFor(() => this.logs.length > 0);
  79. const logFilesResponses = await Promise.all(
  80. this.logs.map(async log => ({
  81. name: log.log_name,
  82. content: await apiCaller.send({
  83. url: generateUrlForLog(log.log_name, startDate, endDate),
  84. }),
  85. }))
  86. );
  87. const zip = new JSZip();
  88. logFilesResponses.forEach(response => {
  89. zip.file(`${response.name}.log`, response.content.data);
  90. });
  91. await downloadDiagnosticsIntoZip(zip);
  92. const zipContent = await zip.generateAsync({
  93. type: "blob",
  94. compression: "DEFLATE",
  95. });
  96. saveAs(zipContent, "logs.zip");
  97. runInAction(() => {
  98. this.downloadingAllLogs = false;
  99. });
  100. }
  101. @action download(
  102. logName: string,
  103. startDate?: Date | null,
  104. endDate?: Date | null
  105. ) {
  106. DomUtils.executeDownloadLink(
  107. generateUrlForLog(logName, startDate, endDate)
  108. );
  109. }
  110. @action async downloadDiagnostics() {
  111. this.generatingDiagnostics = true;
  112. const zip = new JSZip();
  113. await downloadDiagnosticsIntoZip(zip);
  114. const zipContent = await zip.generateAsync({
  115. type: "blob",
  116. compression: "DEFLATE",
  117. });
  118. saveAs(zipContent, "diagnostics.zip");
  119. runInAction(() => {
  120. this.generatingDiagnostics = false;
  121. });
  122. }
  123. socket!: WebSocket;
  124. startLiveFeed(options: { logName: string; severityLevel: number }) {
  125. const { logName, severityLevel } = options;
  126. const token = cookie.get("token") || "null";
  127. let wsUrl;
  128. if (configLoader.config.servicesUrls.coriolisLogStreamBaseUrl === "") {
  129. wsUrl = `wss://${window.location.host}`;
  130. } else {
  131. wsUrl = configLoader.config.servicesUrls.coriolisLogStreamBaseUrl.replace(
  132. "https",
  133. "wss"
  134. );
  135. }
  136. let url = `${wsUrl}/log-stream?auth_type=keystone`;
  137. url += `&auth_token=${token}&severity=${severityLevel}`;
  138. if (logName !== "All Logs") {
  139. url += `&app_name=${logName}`;
  140. }
  141. this.socket = new WebSocket(url);
  142. this.socket.onopen = () => {
  143. console.log("WS Log connection open");
  144. };
  145. this.socket.onmessage = e => {
  146. if (typeof e.data === "string") {
  147. this.addToLiveFeed(JSON.parse(e.data));
  148. }
  149. };
  150. this.socket.onclose = () => {
  151. console.log("WS Log connection closed");
  152. };
  153. this.socket.onerror = (e: any) => {
  154. notificationStore.alert(`WebSocket error: ${e.message}`, "error");
  155. };
  156. }
  157. @action addToLiveFeed(feed: { message: string }) {
  158. this.liveFeed = [...this.liveFeed, feed.message];
  159. if (this.liveFeed.length > MAX_STREAM_LINES) {
  160. this.liveFeed = [
  161. ...this.liveFeed.filter(
  162. (_, i) => i > this.liveFeed.length - MAX_STREAM_LINES
  163. ),
  164. ];
  165. }
  166. }
  167. @action clearLiveFeed() {
  168. this.liveFeed = [];
  169. }
  170. @action stopLiveFeed() {
  171. if (this.socket) {
  172. this.socket.close();
  173. }
  174. }
  175. }
  176. export default new LogStore();