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
+
+ Download system logs
+
+
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
}