mirror of
https://github.com/appwrite/appwrite
synced 2026-05-23 00:49:02 +00:00
Merge pull request #9773 from samikshaaagarwal/samiksha/9510
Truncate logs in function worker
This commit is contained in:
commit
36049cf9aa
7 changed files with 130 additions and 8 deletions
|
|
@ -1917,7 +1917,7 @@ return [
|
|||
'$id' => ID::custom('errors'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 1000000,
|
||||
'size' => APP_FUNCTION_ERROR_LENGTH_LIMIT,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
|
|
@ -1928,7 +1928,7 @@ return [
|
|||
'$id' => ID::custom('logs'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 1000000,
|
||||
'size' => APP_FUNCTION_LOG_LENGTH_LIMIT,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
|
|
|
|||
|
|
@ -615,11 +615,33 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
|||
}
|
||||
}
|
||||
|
||||
// Truncate logs if they exceed the limit
|
||||
$maxLogLength = APP_FUNCTION_LOG_LENGTH_LIMIT;
|
||||
$logs = $executionResponse['logs'] ?? '';
|
||||
|
||||
if (\is_string($logs) && \strlen($logs) > $maxLogLength) {
|
||||
$warningMessage = "[WARNING] Logs truncated. The output exceeded {$maxLogLength} characters.\n";
|
||||
$warningLength = \strlen($warningMessage);
|
||||
$maxContentLength = $maxLogLength - $warningLength;
|
||||
$logs = $warningMessage . \substr($logs, -$maxContentLength);
|
||||
}
|
||||
|
||||
// Truncate errors if they exceed the limit
|
||||
$maxErrorLength = APP_FUNCTION_ERROR_LENGTH_LIMIT;
|
||||
$errors = $executionResponse['errors'] ?? '';
|
||||
|
||||
if (\is_string($errors) && \strlen($errors) > $maxErrorLength) {
|
||||
$warningMessage = "[WARNING] Errors truncated. The output exceeded {$maxErrorLength} characters.\n";
|
||||
$warningLength = \strlen($warningMessage);
|
||||
$maxContentLength = $maxErrorLength - $warningLength;
|
||||
$errors = $warningMessage . \substr($errors, -$maxContentLength);
|
||||
}
|
||||
|
||||
/** Update execution status */
|
||||
$status = $executionResponse['statusCode'] >= 500 ? 'failed' : 'completed';
|
||||
$execution->setAttribute('status', $status);
|
||||
$execution->setAttribute('logs', $executionResponse['logs']);
|
||||
$execution->setAttribute('errors', $executionResponse['errors']);
|
||||
$execution->setAttribute('logs', $logs);
|
||||
$execution->setAttribute('errors', $errors);
|
||||
$execution->setAttribute('responseStatusCode', $executionResponse['statusCode']);
|
||||
$execution->setAttribute('responseHeaders', $headersFiltered);
|
||||
$execution->setAttribute('duration', $executionResponse['duration']);
|
||||
|
|
|
|||
|
|
@ -143,6 +143,8 @@ const APP_AUTH_TYPE_KEY = 'Key';
|
|||
const APP_AUTH_TYPE_ADMIN = 'Admin';
|
||||
// Response related
|
||||
const MAX_OUTPUT_CHUNK_SIZE = 10 * 1024 * 1024; // 10MB
|
||||
const APP_FUNCTION_LOG_LENGTH_LIMIT = 1000000;
|
||||
const APP_FUNCTION_ERROR_LENGTH_LIMIT = 1000000;
|
||||
// Function headers
|
||||
const FUNCTION_ALLOWLIST_HEADERS_REQUEST = ['content-type', 'agent', 'content-length', 'host'];
|
||||
const FUNCTION_ALLOWLIST_HEADERS_RESPONSE = ['content-type', 'content-length'];
|
||||
|
|
|
|||
|
|
@ -423,13 +423,34 @@ class Create extends Base
|
|||
}
|
||||
}
|
||||
|
||||
$maxLogLength = APP_FUNCTION_LOG_LENGTH_LIMIT;
|
||||
$logs = $executionResponse['logs'] ?? '';
|
||||
|
||||
if (\is_string($logs) && \strlen($logs) > $maxLogLength) {
|
||||
$warningMessage = "[WARNING] Logs truncated. The output exceeded {$maxLogLength} characters.\n";
|
||||
$warningLength = \strlen($warningMessage);
|
||||
$maxContentLength = $maxLogLength - $warningLength;
|
||||
$logs = $warningMessage . \substr($logs, -$maxContentLength);
|
||||
}
|
||||
|
||||
// Truncate errors if they exceed the limit
|
||||
$maxErrorLength = APP_FUNCTION_ERROR_LENGTH_LIMIT;
|
||||
$errors = $executionResponse['errors'] ?? '';
|
||||
|
||||
if (\is_string($errors) && \strlen($errors) > $maxErrorLength) {
|
||||
$warningMessage = "[WARNING] Errors truncated. The output exceeded {$maxErrorLength} characters.\n";
|
||||
$warningLength = \strlen($warningMessage);
|
||||
$maxContentLength = $maxErrorLength - $warningLength;
|
||||
$errors = $warningMessage . \substr($errors, -$maxContentLength);
|
||||
}
|
||||
|
||||
/** Update execution status */
|
||||
$status = $executionResponse['statusCode'] >= 500 ? 'failed' : 'completed';
|
||||
$execution->setAttribute('status', $status);
|
||||
$execution->setAttribute('responseStatusCode', $executionResponse['statusCode']);
|
||||
$execution->setAttribute('responseHeaders', $headersFiltered);
|
||||
$execution->setAttribute('logs', $executionResponse['logs']);
|
||||
$execution->setAttribute('errors', $executionResponse['errors']);
|
||||
$execution->setAttribute('logs', $logs);
|
||||
$execution->setAttribute('errors', $errors);
|
||||
$execution->setAttribute('duration', $executionResponse['duration']);
|
||||
} catch (\Throwable $th) {
|
||||
$durationEnd = \microtime(true);
|
||||
|
|
|
|||
|
|
@ -550,14 +550,36 @@ class Functions extends Action
|
|||
}
|
||||
}
|
||||
|
||||
$maxLogLength = APP_FUNCTION_LOG_LENGTH_LIMIT;
|
||||
$logs = $executionResponse['logs'] ?? '';
|
||||
|
||||
if (\is_string($logs) && \strlen($logs) > $maxLogLength) {
|
||||
$warningMessage = "[WARNING] Logs truncated. The output exceeded {$maxLogLength} characters.\n";
|
||||
$warningLength = \strlen($warningMessage);
|
||||
$maxContentLength = $maxLogLength - $warningLength;
|
||||
$logs = $warningMessage . \substr($logs, -$maxContentLength);
|
||||
}
|
||||
|
||||
// Truncate errors if they exceed the limit
|
||||
$maxErrorLength = APP_FUNCTION_ERROR_LENGTH_LIMIT;
|
||||
$errors = $executionResponse['errors'] ?? '';
|
||||
|
||||
if (\is_string($errors) && \strlen($errors) > $maxErrorLength) {
|
||||
$warningMessage = "[WARNING] Errors truncated. The output exceeded {$maxErrorLength} characters.\n";
|
||||
$warningLength = \strlen($warningMessage);
|
||||
$maxContentLength = $maxErrorLength - $warningLength;
|
||||
$errors = $warningMessage . \substr($errors, -$maxContentLength);
|
||||
}
|
||||
|
||||
/** Update execution status */
|
||||
$execution
|
||||
->setAttribute('status', $status)
|
||||
->setAttribute('responseStatusCode', $executionResponse['statusCode'])
|
||||
->setAttribute('responseHeaders', $headersFiltered)
|
||||
->setAttribute('logs', $executionResponse['logs'])
|
||||
->setAttribute('errors', $executionResponse['errors'])
|
||||
->setAttribute('logs', $logs)
|
||||
->setAttribute('errors', $errors)
|
||||
->setAttribute('duration', $executionResponse['duration']);
|
||||
|
||||
} catch (\Throwable $th) {
|
||||
$durationEnd = \microtime(true);
|
||||
$execution
|
||||
|
|
|
|||
|
|
@ -2282,4 +2282,45 @@ class FunctionsCustomServerTest extends Scope
|
|||
|
||||
$this->cleanupFunction($functionId);
|
||||
}
|
||||
|
||||
public function testLogAndErrorTruncation(): void
|
||||
{
|
||||
$functionId = $this->setupFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'name' => 'Test Log Truncation',
|
||||
'runtime' => 'node-22',
|
||||
'entrypoint' => 'index.js',
|
||||
'timeout' => 15,
|
||||
]);
|
||||
|
||||
$this->setupDeployment($functionId, [
|
||||
'code' => $this->packageFunction('log-error-truncation'),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
$execution = $this->createExecution($functionId, [
|
||||
'async' => 'false'
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $execution['headers']['status-code']);
|
||||
$this->assertEquals(200, $execution['body']['responseStatusCode']);
|
||||
|
||||
// Verify logs are truncated and warning message is present at the beginning
|
||||
$logs = $execution['body']['logs'];
|
||||
$this->assertLessThanOrEqual(APP_FUNCTION_LOG_LENGTH_LIMIT, strlen($logs));
|
||||
$this->assertStringStartsWith('[WARNING] Logs truncated', $logs);
|
||||
|
||||
$this->assertStringNotContainsString('z', $logs);
|
||||
$this->assertStringContainsString('a', $logs);
|
||||
|
||||
// Verify errors are truncated and warning message is present at the beginning
|
||||
$errors = $execution['body']['errors'];
|
||||
$this->assertLessThanOrEqual(APP_FUNCTION_ERROR_LENGTH_LIMIT, strlen($errors));
|
||||
$this->assertStringStartsWith('[WARNING] Errors truncated', $errors);
|
||||
|
||||
$this->assertStringNotContainsString('z', $errors);
|
||||
$this->assertStringContainsString('a', $errors);
|
||||
|
||||
$this->cleanupFunction($functionId);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
14
tests/resources/functions/log-error-truncation/index.js
Normal file
14
tests/resources/functions/log-error-truncation/index.js
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
module.exports = async(context) => {
|
||||
// Create a string that is 1000001 characters long (exceeds the 1000000 limit)
|
||||
const longString = 'z' + 'a'.repeat(1000000);
|
||||
|
||||
context.log(longString);
|
||||
context.error(longString);
|
||||
|
||||
return context.res.json({
|
||||
motto: 'Build like a team of hundreds_',
|
||||
learn: 'https://appwrite.io/docs',
|
||||
connect: 'https://appwrite.io/discord',
|
||||
getInspired: 'https://builtwith.appwrite.io',
|
||||
});
|
||||
};
|
||||
Loading…
Reference in a new issue