From d33663f5acaf0b655846b74e91d4a392de5d546b Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 4 Jul 2023 11:54:01 +0100 Subject: [PATCH 01/24] Store logs to file --- packages/backend-core/src/logging/pino/logger.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/backend-core/src/logging/pino/logger.ts b/packages/backend-core/src/logging/pino/logger.ts index c96bc83e04..6bcbf481c5 100644 --- a/packages/backend-core/src/logging/pino/logger.ts +++ b/packages/backend-core/src/logging/pino/logger.ts @@ -5,6 +5,9 @@ import * as correlation from "../correlation" import { IdentityType } from "@budibase/types" import { LOG_CONTEXT } from "../index" +import path from "path" +import { budibaseTempDir } from "../../objectStore" + // LOGGER let pinoInstance: pino.Logger | undefined @@ -29,6 +32,13 @@ if (!env.DISABLE_PINO_LOGGER) { singleLine: true, }, } + } else { + pinoOptions.transport = { + target: "pino/file", + options: { + destination: path.join(budibaseTempDir(), "pino.logs"), + }, + } } pinoInstance = pino(pinoOptions) From ff67df8e795d37b849f0c5df0f65959515c51764 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 4 Jul 2023 17:07:58 +0100 Subject: [PATCH 02/24] Rotate logs --- packages/backend-core/package.json | 1 + .../backend-core/src/logging/pino/logger.ts | 36 +++++++++++++------ yarn.lock | 5 +++ 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 4a1ed5c373..7f3c064c92 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -51,6 +51,7 @@ "pouchdb": "7.3.0", "pouchdb-find": "7.2.2", "redlock": "4.2.0", + "rotating-file-stream": "3.1.0", "sanitize-s3-objectkey": "0.0.1", "semver": "7.3.7", "tar-fs": "2.1.1", diff --git a/packages/backend-core/src/logging/pino/logger.ts b/packages/backend-core/src/logging/pino/logger.ts index 6bcbf481c5..8d3329ce8a 100644 --- a/packages/backend-core/src/logging/pino/logger.ts +++ b/packages/backend-core/src/logging/pino/logger.ts @@ -1,12 +1,17 @@ -import env from "../../environment" import pino, { LoggerOptions } from "pino" +import path from "path" +import fs from "fs" +import * as rfs from "rotating-file-stream" + +import { IdentityType } from "@budibase/types" + +import env from "../../environment" import * as context from "../../context" import * as correlation from "../correlation" -import { IdentityType } from "@budibase/types" import { LOG_CONTEXT } from "../index" -import path from "path" import { budibaseTempDir } from "../../objectStore" +import environment from "../../environment" // LOGGER @@ -25,7 +30,8 @@ if (!env.DISABLE_PINO_LOGGER) { timestamp: () => `,"timestamp":"${new Date(Date.now()).toISOString()}"`, } - if (env.isDev()) { + let outFile: rfs.RotatingFileStream | undefined + if (!env.isDev()) { pinoOptions.transport = { target: "pino-pretty", options: { @@ -33,15 +39,23 @@ if (!env.DISABLE_PINO_LOGGER) { }, } } else { - pinoOptions.transport = { - target: "pino/file", - options: { - destination: path.join(budibaseTempDir(), "pino.logs"), - }, - } + const fileName = path.join( + budibaseTempDir(), + "logs", + `${environment.SERVICE_NAME}.logs` + ) + outFile = rfs.createStream(fileName, { + size: "10M", + + teeToStdout: true, + }) + + outFile.on("rotation", () => { + fs.copyFileSync(fileName, `${fileName}.bak`) + }) } - pinoInstance = pino(pinoOptions) + pinoInstance = outFile ? pino(pinoOptions, outFile) : pino(pinoOptions) // CONSOLE OVERRIDES diff --git a/yarn.lock b/yarn.lock index 9537bb81fa..c6c6235d02 100644 --- a/yarn.lock +++ b/yarn.lock @@ -23197,6 +23197,11 @@ rollup@^3.18.0: optionalDependencies: fsevents "~2.3.2" +rotating-file-stream@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/rotating-file-stream/-/rotating-file-stream-3.1.0.tgz#6cf50e1671de82a396de6d31d39a6f2445f45fba" + integrity sha512-TkMF6cP1/QDcon9D71mjxHoflNuznNOrY5JJQfuxkKklZRmoow/lWBLNxXVjb6KcjAU8BDCV145buLgOx9Px1Q== + rrweb-cssom@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" From a50834127a4a065fa96f07c517dc08829bbc8c6b Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 5 Jul 2023 10:18:58 +0200 Subject: [PATCH 03/24] Use destinations instead of transports --- .../backend-core/src/logging/pino/logger.ts | 54 +++++++++++-------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/packages/backend-core/src/logging/pino/logger.ts b/packages/backend-core/src/logging/pino/logger.ts index 8d3329ce8a..7667bf83a9 100644 --- a/packages/backend-core/src/logging/pino/logger.ts +++ b/packages/backend-core/src/logging/pino/logger.ts @@ -12,6 +12,7 @@ import { LOG_CONTEXT } from "../index" import { budibaseTempDir } from "../../objectStore" import environment from "../../environment" +import pinoPretty from "pino-pretty" // LOGGER @@ -30,32 +31,20 @@ if (!env.DISABLE_PINO_LOGGER) { timestamp: () => `,"timestamp":"${new Date(Date.now()).toISOString()}"`, } - let outFile: rfs.RotatingFileStream | undefined - if (!env.isDev()) { - pinoOptions.transport = { - target: "pino-pretty", - options: { - singleLine: true, - }, - } - } else { - const fileName = path.join( - budibaseTempDir(), - "logs", - `${environment.SERVICE_NAME}.logs` - ) - outFile = rfs.createStream(fileName, { - size: "10M", + const destinations: pino.DestinationStream[] = [] - teeToStdout: true, - }) - - outFile.on("rotation", () => { - fs.copyFileSync(fileName, `${fileName}.bak`) - }) + if (env.isDev()) { + destinations.push(pinoPretty({ singleLine: true })) } - pinoInstance = outFile ? pino(pinoOptions, outFile) : pino(pinoOptions) + // TODO + if (true) { + destinations.push(localFileDestination()) + } + + pinoInstance = destinations.length + ? pino(pinoOptions, pino.multistream(destinations)) + : pino(pinoOptions) // CONSOLE OVERRIDES @@ -82,6 +71,25 @@ if (!env.DISABLE_PINO_LOGGER) { return typeof obj === "string" } + function localFileDestination() { + const fileName = path.join( + budibaseTempDir(), + "logs", + `${environment.SERVICE_NAME}.logs` + ) + const outFile = rfs.createStream(fileName, { + size: "10M", + + teeToStdout: true, + }) + + outFile.on("rotation", () => { + fs.copyFileSync(fileName, `${fileName}.bak`) + }) + + return outFile + } + /** * Backwards compatibility between console logging statements * and pino logging requirements. From ef0888fcad6406acd1100a1138aaf58ca8ff21bf Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 5 Jul 2023 10:27:13 +0200 Subject: [PATCH 04/24] Read package name --- packages/backend-core/src/environment.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/backend-core/src/environment.ts b/packages/backend-core/src/environment.ts index eab8cd4c45..0563556160 100644 --- a/packages/backend-core/src/environment.ts +++ b/packages/backend-core/src/environment.ts @@ -47,7 +47,10 @@ function httpLogging() { return process.env.HTTP_LOGGING } -function findVersion() { +function getPackageJsonFields(): { + VERSION: string | undefined + SERVICE_NAME: string | undefined +} { function findFileInAncestors( fileName: string, currentDir: string @@ -69,10 +72,14 @@ function findVersion() { try { const packageJsonFile = findFileInAncestors("package.json", process.cwd()) const content = readFileSync(packageJsonFile!, "utf-8") - return JSON.parse(content).version + const parsedContent = JSON.parse(content) + return { + VERSION: parsedContent.version, + SERVICE_NAME: parsedContent.name.replace(/@budibase\//, ""), + } } catch { // throwing an error here is confusing/causes backend-core to be hard to import - return undefined + return { VERSION: undefined, SERVICE_NAME: undefined } } } @@ -154,7 +161,7 @@ const environment = { ENABLE_SSO_MAINTENANCE_MODE: selfHosted ? process.env.ENABLE_SSO_MAINTENANCE_MODE : false, - VERSION: findVersion(), + ...getPackageJsonFields(), DISABLE_PINO_LOGGER: process.env.DISABLE_PINO_LOGGER, _set(key: any, value: any) { process.env[key] = value From 2411b59709c217baa1e0d0f922689085d5fb9434 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 5 Jul 2023 13:15:09 +0200 Subject: [PATCH 05/24] Unify logs --- packages/backend-core/src/logging/pino/logger.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/backend-core/src/logging/pino/logger.ts b/packages/backend-core/src/logging/pino/logger.ts index 7667bf83a9..7c35dbeff5 100644 --- a/packages/backend-core/src/logging/pino/logger.ts +++ b/packages/backend-core/src/logging/pino/logger.ts @@ -11,7 +11,6 @@ import * as correlation from "../correlation" import { LOG_CONTEXT } from "../index" import { budibaseTempDir } from "../../objectStore" -import environment from "../../environment" import pinoPretty from "pino-pretty" // LOGGER @@ -72,14 +71,9 @@ if (!env.DISABLE_PINO_LOGGER) { } function localFileDestination() { - const fileName = path.join( - budibaseTempDir(), - "logs", - `${environment.SERVICE_NAME}.logs` - ) + const fileName = path.join(budibaseTempDir(), "logs", `budibase.logs`) const outFile = rfs.createStream(fileName, { size: "10M", - teeToStdout: true, }) From b0f2306c5046cb17d3ad462f8445b39bc6253c05 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 5 Jul 2023 16:43:23 +0200 Subject: [PATCH 06/24] Fix types --- packages/backend-core/src/environment.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/backend-core/src/environment.ts b/packages/backend-core/src/environment.ts index 0563556160..971c5941ff 100644 --- a/packages/backend-core/src/environment.ts +++ b/packages/backend-core/src/environment.ts @@ -48,8 +48,8 @@ function httpLogging() { } function getPackageJsonFields(): { - VERSION: string | undefined - SERVICE_NAME: string | undefined + VERSION: string + SERVICE_NAME: string } { function findFileInAncestors( fileName: string, @@ -79,7 +79,7 @@ function getPackageJsonFields(): { } } catch { // throwing an error here is confusing/causes backend-core to be hard to import - return { VERSION: undefined, SERVICE_NAME: undefined } + return { VERSION: "", SERVICE_NAME: "" } } } From e351a77f753b41c3671f5e79cad516a0c8a9469a Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 5 Jul 2023 16:51:42 +0200 Subject: [PATCH 07/24] Decorate service --- packages/backend-core/src/environment.ts | 3 ++- packages/backend-core/src/logging/pino/logger.ts | 11 ++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/backend-core/src/environment.ts b/packages/backend-core/src/environment.ts index 971c5941ff..5a230f6925 100644 --- a/packages/backend-core/src/environment.ts +++ b/packages/backend-core/src/environment.ts @@ -75,7 +75,7 @@ function getPackageJsonFields(): { const parsedContent = JSON.parse(content) return { VERSION: parsedContent.version, - SERVICE_NAME: parsedContent.name.replace(/@budibase\//, ""), + SERVICE_NAME: parsedContent.name, } } catch { // throwing an error here is confusing/causes backend-core to be hard to import @@ -168,6 +168,7 @@ const environment = { // @ts-ignore environment[key] = value }, + ROLLING_LOG_MAX_SIZE: process.env.ROLLING_LOG_MAX_SIZE || "100M", } // clean up any environment variable edge cases diff --git a/packages/backend-core/src/logging/pino/logger.ts b/packages/backend-core/src/logging/pino/logger.ts index 7c35dbeff5..e2e6067bbe 100644 --- a/packages/backend-core/src/logging/pino/logger.ts +++ b/packages/backend-core/src/logging/pino/logger.ts @@ -24,7 +24,9 @@ if (!env.DISABLE_PINO_LOGGER) { return { level: label.toUpperCase() } }, bindings: () => { - return {} + return { + service: env.SERVICE_NAME, + } }, }, timestamp: () => `,"timestamp":"${new Date(Date.now()).toISOString()}"`, @@ -36,8 +38,7 @@ if (!env.DISABLE_PINO_LOGGER) { destinations.push(pinoPretty({ singleLine: true })) } - // TODO - if (true) { + if (env.SELF_HOSTED) { destinations.push(localFileDestination()) } @@ -71,9 +72,9 @@ if (!env.DISABLE_PINO_LOGGER) { } function localFileDestination() { - const fileName = path.join(budibaseTempDir(), "logs", `budibase.logs`) + const fileName = path.join(budibaseTempDir(), `budibase.logs`) const outFile = rfs.createStream(fileName, { - size: "10M", + size: env.ROLLING_LOG_MAX_SIZE, teeToStdout: true, }) From 580985c7beebe5ff7222e986ea11cfc12eebdce4 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 5 Jul 2023 16:57:00 +0200 Subject: [PATCH 08/24] Extract code --- .../backend-core/src/logging/localLogging.ts | 20 ++++++++++++++++++ .../backend-core/src/logging/pino/logger.ts | 21 ++----------------- 2 files changed, 22 insertions(+), 19 deletions(-) create mode 100644 packages/backend-core/src/logging/localLogging.ts diff --git a/packages/backend-core/src/logging/localLogging.ts b/packages/backend-core/src/logging/localLogging.ts new file mode 100644 index 0000000000..44bacd28cd --- /dev/null +++ b/packages/backend-core/src/logging/localLogging.ts @@ -0,0 +1,20 @@ +import fs from "fs" +import path from "path" +import * as rfs from "rotating-file-stream" + +import { env } from "process" +import { budibaseTempDir } from "src/objectStore" + +export function localFileDestination() { + const fileName = path.join(budibaseTempDir(), `budibase.logs`) + const outFile = rfs.createStream(fileName, { + size: env.ROLLING_LOG_MAX_SIZE, + teeToStdout: true, + }) + + outFile.on("rotation", () => { + fs.copyFileSync(fileName, `${fileName}.bak`) + }) + + return outFile +} diff --git a/packages/backend-core/src/logging/pino/logger.ts b/packages/backend-core/src/logging/pino/logger.ts index e2e6067bbe..0689ba6af2 100644 --- a/packages/backend-core/src/logging/pino/logger.ts +++ b/packages/backend-core/src/logging/pino/logger.ts @@ -1,7 +1,5 @@ import pino, { LoggerOptions } from "pino" -import path from "path" -import fs from "fs" -import * as rfs from "rotating-file-stream" +import pinoPretty from "pino-pretty" import { IdentityType } from "@budibase/types" @@ -10,8 +8,7 @@ import * as context from "../../context" import * as correlation from "../correlation" import { LOG_CONTEXT } from "../index" -import { budibaseTempDir } from "../../objectStore" -import pinoPretty from "pino-pretty" +import { localFileDestination } from "../localLogging" // LOGGER @@ -71,20 +68,6 @@ if (!env.DISABLE_PINO_LOGGER) { return typeof obj === "string" } - function localFileDestination() { - const fileName = path.join(budibaseTempDir(), `budibase.logs`) - const outFile = rfs.createStream(fileName, { - size: env.ROLLING_LOG_MAX_SIZE, - teeToStdout: true, - }) - - outFile.on("rotation", () => { - fs.copyFileSync(fileName, `${fileName}.bak`) - }) - - return outFile - } - /** * Backwards compatibility between console logging statements * and pino logging requirements. From 61abb7541ac750d47e8ae006c778bd2fa660bb6d Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 5 Jul 2023 16:59:18 +0200 Subject: [PATCH 09/24] Fix imports --- packages/backend-core/src/logging/localLogging.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend-core/src/logging/localLogging.ts b/packages/backend-core/src/logging/localLogging.ts index 44bacd28cd..bd42ce08d1 100644 --- a/packages/backend-core/src/logging/localLogging.ts +++ b/packages/backend-core/src/logging/localLogging.ts @@ -2,8 +2,8 @@ import fs from "fs" import path from "path" import * as rfs from "rotating-file-stream" -import { env } from "process" -import { budibaseTempDir } from "src/objectStore" +import env from "../environment" +import { budibaseTempDir } from "../objectStore" export function localFileDestination() { const fileName = path.join(budibaseTempDir(), `budibase.logs`) From bb28d09eababb682930d53512d931efd09188d2c Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 10 Jul 2023 13:25:24 +0200 Subject: [PATCH 10/24] Add logs endpoint --- packages/backend-core/src/logging/index.ts | 1 + .../backend-core/src/logging/localLogging.ts | 20 ----------- .../backend-core/src/logging/pino/logger.ts | 2 +- packages/backend-core/src/logging/system.ts | 33 +++++++++++++++++++ packages/frontend-core/src/api/index.js | 2 ++ packages/frontend-core/src/api/logs.js | 14 ++++++++ packages/frontend-core/src/utils/download.js | 14 ++++++++ packages/frontend-core/src/utils/index.js | 2 +- .../worker/src/api/controllers/system/logs.ts | 8 +++++ packages/worker/src/api/routes/index.ts | 2 ++ packages/worker/src/api/routes/system/logs.ts | 9 +++++ 11 files changed, 85 insertions(+), 22 deletions(-) delete mode 100644 packages/backend-core/src/logging/localLogging.ts create mode 100644 packages/backend-core/src/logging/system.ts create mode 100644 packages/frontend-core/src/api/logs.js create mode 100644 packages/worker/src/api/controllers/system/logs.ts create mode 100644 packages/worker/src/api/routes/system/logs.ts diff --git a/packages/backend-core/src/logging/index.ts b/packages/backend-core/src/logging/index.ts index b87062c478..f7e1c4fa41 100644 --- a/packages/backend-core/src/logging/index.ts +++ b/packages/backend-core/src/logging/index.ts @@ -1,6 +1,7 @@ export * as correlation from "./correlation/correlation" export { logger } from "./pino/logger" export * from "./alerts" +export * as system from "./system" // turn off or on context logging i.e. tenantId, appId etc export let LOG_CONTEXT = true diff --git a/packages/backend-core/src/logging/localLogging.ts b/packages/backend-core/src/logging/localLogging.ts deleted file mode 100644 index bd42ce08d1..0000000000 --- a/packages/backend-core/src/logging/localLogging.ts +++ /dev/null @@ -1,20 +0,0 @@ -import fs from "fs" -import path from "path" -import * as rfs from "rotating-file-stream" - -import env from "../environment" -import { budibaseTempDir } from "../objectStore" - -export function localFileDestination() { - const fileName = path.join(budibaseTempDir(), `budibase.logs`) - const outFile = rfs.createStream(fileName, { - size: env.ROLLING_LOG_MAX_SIZE, - teeToStdout: true, - }) - - outFile.on("rotation", () => { - fs.copyFileSync(fileName, `${fileName}.bak`) - }) - - return outFile -} diff --git a/packages/backend-core/src/logging/pino/logger.ts b/packages/backend-core/src/logging/pino/logger.ts index 0689ba6af2..0b130068f3 100644 --- a/packages/backend-core/src/logging/pino/logger.ts +++ b/packages/backend-core/src/logging/pino/logger.ts @@ -8,7 +8,7 @@ import * as context from "../../context" import * as correlation from "../correlation" import { LOG_CONTEXT } from "../index" -import { localFileDestination } from "../localLogging" +import { localFileDestination } from "../system" // LOGGER diff --git a/packages/backend-core/src/logging/system.ts b/packages/backend-core/src/logging/system.ts new file mode 100644 index 0000000000..7bca469e5c --- /dev/null +++ b/packages/backend-core/src/logging/system.ts @@ -0,0 +1,33 @@ +import fs from "fs" +import path from "path" +import * as rfs from "rotating-file-stream" + +import env from "../environment" +import { budibaseTempDir } from "../objectStore" + +const logsFileName = path.join(budibaseTempDir(), `budibase.logs`) +const rollingFileName = `${logsFileName}.bak` + +export function localFileDestination() { + const outFile = rfs.createStream(logsFileName, { + size: env.ROLLING_LOG_MAX_SIZE, + teeToStdout: true, + }) + + outFile.on("rotation", () => { + fs.copyFileSync(logsFileName, rollingFileName) + }) + + return outFile +} + +export function getLogReadStream() { + const logsContent = fs.readFileSync(logsFileName) + if (!fs.existsSync(rollingFileName)) { + return logsContent + } + + const rollingContent = fs.readFileSync(rollingFileName) + const combinedContent = Buffer.concat([logsContent, rollingContent]) + return combinedContent +} diff --git a/packages/frontend-core/src/api/index.js b/packages/frontend-core/src/api/index.js index 7b823d28c2..5bffb82f4d 100644 --- a/packages/frontend-core/src/api/index.js +++ b/packages/frontend-core/src/api/index.js @@ -30,6 +30,7 @@ import { buildBackupsEndpoints } from "./backups" import { buildEnvironmentVariableEndpoints } from "./environmentVariables" import { buildEventEndpoints } from "./events" import { buildAuditLogsEndpoints } from "./auditLogs" +import { buildLogsEndpoints } from "./logs" /** * Random identifier to uniquely identify a session in a tab. This is @@ -277,5 +278,6 @@ export const createAPIClient = config => { ...buildEnvironmentVariableEndpoints(API), ...buildEventEndpoints(API), ...buildAuditLogsEndpoints(API), + ...buildLogsEndpoints(API), } } diff --git a/packages/frontend-core/src/api/logs.js b/packages/frontend-core/src/api/logs.js new file mode 100644 index 0000000000..d8c97b4773 --- /dev/null +++ b/packages/frontend-core/src/api/logs.js @@ -0,0 +1,14 @@ +export const buildLogsEndpoints = API => ({ + /** + * Gets a list of datasources. + */ + getServerLogs: async () => { + return await API.get({ + url: "/api/global/system/logs", + json: false, + parseResponse: async response => { + return response + }, + }) + }, +}) diff --git a/packages/frontend-core/src/utils/download.js b/packages/frontend-core/src/utils/download.js index 681bc8648e..5c00a0023e 100644 --- a/packages/frontend-core/src/utils/download.js +++ b/packages/frontend-core/src/utils/download.js @@ -11,3 +11,17 @@ export function downloadText(filename, text) { URL.revokeObjectURL(url) } + +export async function downloadStream(filename, streamResponse) { + const blob = await streamResponse.blob() + const resBlob = new Blob([blob]) + + const blobUrl = URL.createObjectURL(resBlob) + + const link = document.createElement("a") + link.href = blobUrl + link.download = filename + link.click() + + URL.revokeObjectURL(blobUrl) +} diff --git a/packages/frontend-core/src/utils/index.js b/packages/frontend-core/src/utils/index.js index dd04dd6c28..3f00c00e47 100644 --- a/packages/frontend-core/src/utils/index.js +++ b/packages/frontend-core/src/utils/index.js @@ -5,4 +5,4 @@ export * as RoleUtils from "./roles" export * as Utils from "./utils" export { memo, derivedMemo } from "./memo" export { createWebsocket } from "./websocket" -export { downloadText } from "./download" +export * from "./download" diff --git a/packages/worker/src/api/controllers/system/logs.ts b/packages/worker/src/api/controllers/system/logs.ts new file mode 100644 index 0000000000..f38a8f57a7 --- /dev/null +++ b/packages/worker/src/api/controllers/system/logs.ts @@ -0,0 +1,8 @@ +import { UserCtx } from "@budibase/types" +import { logging } from "@budibase/backend-core" + +export async function getLogs(ctx: UserCtx) { + const logReadStream = logging.system.getLogReadStream() + + ctx.body = logReadStream +} diff --git a/packages/worker/src/api/routes/index.ts b/packages/worker/src/api/routes/index.ts index 4131f14c74..31e93c45cc 100644 --- a/packages/worker/src/api/routes/index.ts +++ b/packages/worker/src/api/routes/index.ts @@ -16,6 +16,7 @@ import licenseRoutes from "./global/license" import migrationRoutes from "./system/migrations" import accountRoutes from "./system/accounts" import restoreRoutes from "./system/restore" +import systemLogRoutes from "./system/logs" export const routes: Router[] = [ configRoutes, @@ -37,4 +38,5 @@ export const routes: Router[] = [ restoreRoutes, eventRoutes, pro.scim, + systemLogRoutes, ] diff --git a/packages/worker/src/api/routes/system/logs.ts b/packages/worker/src/api/routes/system/logs.ts new file mode 100644 index 0000000000..98ebcab24e --- /dev/null +++ b/packages/worker/src/api/routes/system/logs.ts @@ -0,0 +1,9 @@ +import Router from "@koa/router" +import { middleware } from "@budibase/backend-core" +import * as controller from "../../controllers/system/logs" + +const router: Router = new Router() + +router.get("/api/global/system/logs", middleware.adminOnly, controller.getLogs) + +export default router From b64ea43d2052d22a3e6f37647b7da3d231d1023b Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 10 Jul 2023 13:38:38 +0200 Subject: [PATCH 11/24] System logs only for self hosted --- packages/worker/src/api/routes/index.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/worker/src/api/routes/index.ts b/packages/worker/src/api/routes/index.ts index 31e93c45cc..cbd6c96558 100644 --- a/packages/worker/src/api/routes/index.ts +++ b/packages/worker/src/api/routes/index.ts @@ -18,6 +18,8 @@ import accountRoutes from "./system/accounts" import restoreRoutes from "./system/restore" import systemLogRoutes from "./system/logs" +import env from "../../environment" + export const routes: Router[] = [ configRoutes, userRoutes, @@ -38,5 +40,8 @@ export const routes: Router[] = [ restoreRoutes, eventRoutes, pro.scim, - systemLogRoutes, ] + +if (env.SELF_HOSTED) { + routes.push(systemLogRoutes) +} From fd0018c1bc74cae2e5112c7ae1b91d459ab04fc3 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 10 Jul 2023 13:57:27 +0200 Subject: [PATCH 12/24] Download logs section --- .../portal/account/systemLogs/index.svelte | 17 +++++++++++++++++ packages/builder/src/stores/portal/menu.js | 7 +++++++ packages/frontend-core/src/utils/download.js | 11 ++++++++++- .../worker/src/api/controllers/system/logs.ts | 5 ++++- 4 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 packages/builder/src/pages/builder/portal/account/systemLogs/index.svelte diff --git a/packages/builder/src/pages/builder/portal/account/systemLogs/index.svelte b/packages/builder/src/pages/builder/portal/account/systemLogs/index.svelte new file mode 100644 index 0000000000..357970aebe --- /dev/null +++ b/packages/builder/src/pages/builder/portal/account/systemLogs/index.svelte @@ -0,0 +1,17 @@ + + + + Download your latest logs to share with the Budibase team +
+ +
+
diff --git a/packages/builder/src/stores/portal/menu.js b/packages/builder/src/stores/portal/menu.js index 40580cd0ec..04151f66d4 100644 --- a/packages/builder/src/stores/portal/menu.js +++ b/packages/builder/src/stores/portal/menu.js @@ -85,6 +85,13 @@ export const menu = derived([admin, auth], ([$admin, $auth]) => { title: "Audit Logs", href: "/builder/portal/account/auditLogs", }) + + if (!$admin.cloud) { + accountSubPages.push({ + title: "System Logs", + href: "/builder/portal/account/systemLogs", + }) + } } if ($admin.cloud && $auth?.user?.accountPortalAccess) { accountSubPages.push({ diff --git a/packages/frontend-core/src/utils/download.js b/packages/frontend-core/src/utils/download.js index 5c00a0023e..89c8572253 100644 --- a/packages/frontend-core/src/utils/download.js +++ b/packages/frontend-core/src/utils/download.js @@ -12,8 +12,17 @@ export function downloadText(filename, text) { URL.revokeObjectURL(url) } -export async function downloadStream(filename, streamResponse) { +export async function downloadStream(streamResponse) { const blob = await streamResponse.blob() + + const contentDisposition = streamResponse.headers.get("Content-Disposition") + + const matches = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec( + contentDisposition + ) + + const filename = matches[1].replace(/['"]/g, "") + const resBlob = new Blob([blob]) const blobUrl = URL.createObjectURL(resBlob) diff --git a/packages/worker/src/api/controllers/system/logs.ts b/packages/worker/src/api/controllers/system/logs.ts index f38a8f57a7..6b3c70f664 100644 --- a/packages/worker/src/api/controllers/system/logs.ts +++ b/packages/worker/src/api/controllers/system/logs.ts @@ -1,8 +1,11 @@ import { UserCtx } from "@budibase/types" -import { logging } from "@budibase/backend-core" +import { context, logging } from "@budibase/backend-core" export async function getLogs(ctx: UserCtx) { const logReadStream = logging.system.getLogReadStream() + const fileName = `${context.getTenantId()}-${Date.now()}.logs` + + ctx.set("content-disposition", `attachment; filename=${fileName}`) ctx.body = logReadStream } From 8375ea4c5af3a21d24e986069810605b40e67ea5 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 10 Jul 2023 15:59:59 +0200 Subject: [PATCH 13/24] Fix order --- hosting/.env | 1 + packages/backend-core/src/logging/system.ts | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hosting/.env b/hosting/.env index c2b6d55eef..8a0756c0e3 100644 --- a/hosting/.env +++ b/hosting/.env @@ -28,3 +28,4 @@ BB_ADMIN_USER_PASSWORD= # A path that is watched for plugin bundles. Any bundles found are imported automatically/ PLUGINS_DIR= +ROLLING_LOG_MAX_SIZE= \ No newline at end of file diff --git a/packages/backend-core/src/logging/system.ts b/packages/backend-core/src/logging/system.ts index 7bca469e5c..98f2b155a5 100644 --- a/packages/backend-core/src/logging/system.ts +++ b/packages/backend-core/src/logging/system.ts @@ -11,7 +11,6 @@ const rollingFileName = `${logsFileName}.bak` export function localFileDestination() { const outFile = rfs.createStream(logsFileName, { size: env.ROLLING_LOG_MAX_SIZE, - teeToStdout: true, }) outFile.on("rotation", () => { @@ -28,6 +27,6 @@ export function getLogReadStream() { } const rollingContent = fs.readFileSync(rollingFileName) - const combinedContent = Buffer.concat([logsContent, rollingContent]) + const combinedContent = Buffer.concat([rollingContent, logsContent]) return combinedContent } From 4b32c519242463f3d9069d81b6b544f2f4859ac3 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 10 Jul 2023 16:00:16 +0200 Subject: [PATCH 14/24] Prevent spamming button --- .../portal/account/systemLogs/index.svelte | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/packages/builder/src/pages/builder/portal/account/systemLogs/index.svelte b/packages/builder/src/pages/builder/portal/account/systemLogs/index.svelte index 357970aebe..228fc31a6c 100644 --- a/packages/builder/src/pages/builder/portal/account/systemLogs/index.svelte +++ b/packages/builder/src/pages/builder/portal/account/systemLogs/index.svelte @@ -1,17 +1,40 @@ Download your latest logs to share with the Budibase team
- +
+ + From 2585b73723be8a22d3b10950eb41849793c96fde Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 10 Jul 2023 16:52:26 +0200 Subject: [PATCH 15/24] Use native history --- packages/backend-core/src/logging/system.ts | 35 ++++++++++++++------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/packages/backend-core/src/logging/system.ts b/packages/backend-core/src/logging/system.ts index 98f2b155a5..d5d19018c7 100644 --- a/packages/backend-core/src/logging/system.ts +++ b/packages/backend-core/src/logging/system.ts @@ -5,28 +5,41 @@ import * as rfs from "rotating-file-stream" import env from "../environment" import { budibaseTempDir } from "../objectStore" -const logsFileName = path.join(budibaseTempDir(), `budibase.logs`) -const rollingFileName = `${logsFileName}.bak` +const logsFileName = `budibase.logs` +const budibaseLogsHistoryFileName = "budibase-logs-history.txt" + +const logsPath = path.join(budibaseTempDir(), "systemlogs") + +function getFullPath(fileName: string) { + return path.join(logsPath, fileName) +} export function localFileDestination() { const outFile = rfs.createStream(logsFileName, { size: env.ROLLING_LOG_MAX_SIZE, - }) - - outFile.on("rotation", () => { - fs.copyFileSync(logsFileName, rollingFileName) + path: logsPath, + maxFiles: 1, + immutable: true, + history: "budibase-logs-history.txt", + initialRotation: false, }) return outFile } export function getLogReadStream() { - const logsContent = fs.readFileSync(logsFileName) - if (!fs.existsSync(rollingFileName)) { - return logsContent + const streams = [] + const historyFile = getFullPath(budibaseLogsHistoryFileName) + if (fs.existsSync(historyFile)) { + const fileContent = fs.readFileSync(historyFile, "utf-8") + const historyFiles = fileContent.split("\n") + for (const historyFile of historyFiles.filter(x => x)) { + streams.push(fs.readFileSync(historyFile)) + } } - const rollingContent = fs.readFileSync(rollingFileName) - const combinedContent = Buffer.concat([rollingContent, logsContent]) + streams.push(fs.readFileSync(getFullPath(logsFileName))) + + const combinedContent = Buffer.concat(streams) return combinedContent } From e0c0e9bd65f21748a62975da2c8798490060043d Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 10 Jul 2023 17:17:45 +0200 Subject: [PATCH 16/24] Clean --- packages/backend-core/src/logging/system.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend-core/src/logging/system.ts b/packages/backend-core/src/logging/system.ts index d5d19018c7..5f9faa9163 100644 --- a/packages/backend-core/src/logging/system.ts +++ b/packages/backend-core/src/logging/system.ts @@ -20,7 +20,7 @@ export function localFileDestination() { path: logsPath, maxFiles: 1, immutable: true, - history: "budibase-logs-history.txt", + history: budibaseLogsHistoryFileName, initialRotation: false, }) From 6bd41574850531faac407b450e97a9aec979354d Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 10 Jul 2023 17:45:45 +0200 Subject: [PATCH 17/24] Clean --- .../src/pages/builder/portal/account/systemLogs/index.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/pages/builder/portal/account/systemLogs/index.svelte b/packages/builder/src/pages/builder/portal/account/systemLogs/index.svelte index 228fc31a6c..0a59075220 100644 --- a/packages/builder/src/pages/builder/portal/account/systemLogs/index.svelte +++ b/packages/builder/src/pages/builder/portal/account/systemLogs/index.svelte @@ -23,7 +23,7 @@