Skip to content

Commit 8cf079a

Browse files
authored
Return logs in base64 for backward compatibility (#2910)
* Return logs in base64 for backward compatibility * Fix newlines in logs UI * Fix lint * Fix lint
1 parent 97b6482 commit 8cf079a

File tree

5 files changed

+34
-8
lines changed

5 files changed

+34
-8
lines changed

frontend/src/libs/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,15 @@ export const riseRouterException = (status = 404, json = 'Not Found'): never =>
9191
throw new Response(json, { status });
9292
};
9393

94+
export const base64ToArrayBuffer = (base64: string) => {
95+
const binaryString = atob(base64);
96+
const bytes = new Uint8Array(binaryString.length);
97+
for (let i = 0; i < binaryString.length; i++) {
98+
bytes[i] = binaryString.charCodeAt(i);
99+
}
100+
return bytes;
101+
};
102+
94103
export const isValidUrl = (urlString: string) => {
95104
try {
96105
return Boolean(new URL(urlString));

frontend/src/services/project.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import { API } from 'api';
22
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
33

4+
import { base64ToArrayBuffer } from 'libs';
45
import fetchBaseQueryHeaders from 'libs/fetchBaseQueryHeaders';
56

7+
const decoder = new TextDecoder('utf-8');
8+
69
// Helper function to transform backend response to frontend format
710
// eslint-disable-next-line @typescript-eslint/no-explicit-any
811
const transformProjectResponse = (project: any): IProject => ({
@@ -130,7 +133,7 @@ export const projectApi = createApi({
130133
transformResponse: (response: { logs: ILogItem[]; next_token: string }) => {
131134
const logs = response.logs.map((logItem) => ({
132135
...logItem,
133-
message: logItem.message,
136+
message: decoder.decode(base64ToArrayBuffer(logItem.message)),
134137
}));
135138

136139
return {

src/dstack/_internal/server/services/logs/__init__.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@
88
from dstack._internal.server.schemas.logs import PollLogsRequest
99
from dstack._internal.server.schemas.runner import LogEvent as RunnerLogEvent
1010
from dstack._internal.server.services.logs.aws import BOTO_AVAILABLE, CloudWatchLogStorage
11-
from dstack._internal.server.services.logs.base import LogStorage, LogStorageError
11+
from dstack._internal.server.services.logs.base import (
12+
LogStorage,
13+
LogStorageError,
14+
b64encode_raw_message,
15+
)
1216
from dstack._internal.server.services.logs.filelog import FileLogStorage
1317
from dstack._internal.server.services.logs.gcp import GCP_LOGGING_AVAILABLE, GCPLogStorage
1418
from dstack._internal.utils.common import run_async
@@ -75,4 +79,13 @@ def write_logs(
7579

7680

7781
async def poll_logs_async(project: ProjectModel, request: PollLogsRequest) -> JobSubmissionLogs:
78-
return await run_async(get_log_storage().poll_logs, project=project, request=request)
82+
job_submission_logs = await run_async(
83+
get_log_storage().poll_logs, project=project, request=request
84+
)
85+
# Logs are stored in plaintext but transmitted in base64 for API/CLI backward compatibility.
86+
# Old logs stored in base64 are encoded twice for transmission and shown as base64 in CLI/UI.
87+
# We live with that.
88+
# TODO: Drop base64 encoding in 0.20.
89+
for log_event in job_submission_logs.logs:
90+
log_event.message = b64encode_raw_message(log_event.message.encode())
91+
return job_submission_logs

src/dstack/api/_public/runs.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import base64
12
import queue
23
import tempfile
34
import threading
@@ -228,7 +229,7 @@ def logs(
228229
),
229230
)
230231
for log in resp.logs:
231-
yield log.message.encode()
232+
yield base64.b64decode(log.message)
232233
next_token = resp.next_token
233234
if next_token is None:
234235
break

src/tests/_internal/server/routers/test_logs.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,17 +62,17 @@ async def test_returns_logs(
6262
{
6363
"timestamp": "2023-10-06T10:01:53.234234+00:00",
6464
"log_source": "stdout",
65-
"message": "Hello",
65+
"message": "SGVsbG8=",
6666
},
6767
{
6868
"timestamp": "2023-10-06T10:01:53.234235+00:00",
6969
"log_source": "stdout",
70-
"message": "World",
70+
"message": "V29ybGQ=",
7171
},
7272
{
7373
"timestamp": "2023-10-06T10:01:53.234236+00:00",
7474
"log_source": "stdout",
75-
"message": "!",
75+
"message": "IQ==",
7676
},
7777
],
7878
"next_token": None,
@@ -93,7 +93,7 @@ async def test_returns_logs(
9393
{
9494
"timestamp": "2023-10-06T10:01:53.234236+00:00",
9595
"log_source": "stdout",
96-
"message": "!",
96+
"message": "IQ==",
9797
},
9898
],
9999
"next_token": None,

0 commit comments

Comments
 (0)