feat: Add option databaseOptions.clientMetadata to send custom metadata to database server for logging and debugging (#10017)

This commit is contained in:
Copilot
2026-01-24 22:44:38 +01:00
committed by GitHub
parent ba3e7602e6
commit 756c204220
9 changed files with 111 additions and 2 deletions

View File

@@ -30,6 +30,7 @@ const nestedOptionTypes = [
/** The prefix of environment variables for nested options. */ /** The prefix of environment variables for nested options. */
const nestedOptionEnvPrefix = { const nestedOptionEnvPrefix = {
AccountLockoutOptions: 'PARSE_SERVER_ACCOUNT_LOCKOUT_', AccountLockoutOptions: 'PARSE_SERVER_ACCOUNT_LOCKOUT_',
DatabaseOptionsClientMetadata: 'PARSE_SERVER_DATABASE_CLIENT_METADATA_',
CustomPagesOptions: 'PARSE_SERVER_CUSTOM_PAGES_', CustomPagesOptions: 'PARSE_SERVER_CUSTOM_PAGES_',
DatabaseOptions: 'PARSE_SERVER_DATABASE_', DatabaseOptions: 'PARSE_SERVER_DATABASE_',
FileUploadOptions: 'PARSE_SERVER_FILE_UPLOAD_', FileUploadOptions: 'PARSE_SERVER_FILE_UPLOAD_',

View File

@@ -475,4 +475,28 @@ describe_only_db('mongo')('GridFSBucket', () => {
expect(e.message).toEqual('Client must be connected before running operations'); expect(e.message).toEqual('Client must be connected before running operations');
} }
}); });
describe('MongoDB Client Metadata', () => {
it('should not pass metadata to MongoClient by default', async () => {
const gfsAdapter = new GridFSBucketAdapter(databaseURI);
await gfsAdapter._connect();
const driverInfo = gfsAdapter._client.s.options.driverInfo;
// Either driverInfo should be undefined, or it should not contain our custom metadata
if (driverInfo) {
expect(driverInfo.name).toBeUndefined();
}
await gfsAdapter.handleShutdown();
});
it('should pass custom metadata to MongoClient when configured', async () => {
const customMetadata = { name: 'MyParseServer', version: '1.0.0' };
const gfsAdapter = new GridFSBucketAdapter(databaseURI, {
clientMetadata: customMetadata
});
await gfsAdapter._connect();
expect(gfsAdapter._client.s.options.driverInfo.name).toBe(customMetadata.name);
expect(gfsAdapter._client.s.options.driverInfo.version).toBe(customMetadata.version);
await gfsAdapter.handleShutdown();
});
});
}); });

View File

@@ -1063,4 +1063,29 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
await adapter.handleShutdown(); await adapter.handleShutdown();
}); });
}); });
describe('MongoDB Client Metadata', () => {
it('should not pass metadata to MongoClient by default', async () => {
const adapter = new MongoStorageAdapter({ uri: databaseURI });
await adapter.connect();
const driverInfo = adapter.client.s.options.driverInfo;
// Either driverInfo should be undefined, or it should not contain our custom metadata
if (driverInfo) {
expect(driverInfo.name).toBeUndefined();
}
await adapter.handleShutdown();
});
it('should pass custom metadata to MongoClient when configured', async () => {
const customMetadata = { name: 'MyParseServer', version: '1.0.0' };
const adapter = new MongoStorageAdapter({
uri: databaseURI,
mongoOptions: { clientMetadata: customMetadata }
});
await adapter.connect();
expect(adapter.client.s.options.driverInfo.name).toBe(customMetadata.name);
expect(adapter.client.s.options.driverInfo.version).toBe(customMetadata.version);
await adapter.handleShutdown();
});
});
}); });

View File

@@ -17,6 +17,7 @@ export class GridFSBucketAdapter extends FilesAdapter {
_connectionPromise: Promise<Db>; _connectionPromise: Promise<Db>;
_mongoOptions: Object; _mongoOptions: Object;
_algorithm: string; _algorithm: string;
_clientMetadata: ?{ name: string, version: string };
constructor( constructor(
mongoDatabaseURI = defaults.DefaultMongoURI, mongoDatabaseURI = defaults.DefaultMongoURI,
@@ -36,6 +37,7 @@ export class GridFSBucketAdapter extends FilesAdapter {
: null; : null;
const defaultMongoOptions = {}; const defaultMongoOptions = {};
const _mongoOptions = Object.assign(defaultMongoOptions, mongoOptions); const _mongoOptions = Object.assign(defaultMongoOptions, mongoOptions);
this._clientMetadata = mongoOptions.clientMetadata;
// Remove Parse Server-specific options that should not be passed to MongoDB client // Remove Parse Server-specific options that should not be passed to MongoDB client
for (const key of ParseServerDatabaseOptions) { for (const key of ParseServerDatabaseOptions) {
delete _mongoOptions[key]; delete _mongoOptions[key];
@@ -45,7 +47,16 @@ export class GridFSBucketAdapter extends FilesAdapter {
_connect() { _connect() {
if (!this._connectionPromise) { if (!this._connectionPromise) {
this._connectionPromise = MongoClient.connect(this._databaseURI, this._mongoOptions).then( // Only use driverInfo if clientMetadata option is set
const options = { ...this._mongoOptions };
if (this._clientMetadata) {
options.driverInfo = {
name: this._clientMetadata.name,
version: this._clientMetadata.version
};
}
this._connectionPromise = MongoClient.connect(this._databaseURI, options).then(
client => { client => {
this._client = client; this._client = client;
return client.db(client.s.options.dbName); return client.db(client.s.options.dbName);

View File

@@ -134,6 +134,7 @@ export class MongoStorageAdapter implements StorageAdapter {
_onchange: any; _onchange: any;
_stream: any; _stream: any;
_logClientEvents: ?Array<any>; _logClientEvents: ?Array<any>;
_clientMetadata: ?{ name: string, version: string };
// Public // Public
connectionPromise: ?Promise<any>; connectionPromise: ?Promise<any>;
database: any; database: any;
@@ -156,6 +157,7 @@ export class MongoStorageAdapter implements StorageAdapter {
this.schemaCacheTtl = mongoOptions.schemaCacheTtl; this.schemaCacheTtl = mongoOptions.schemaCacheTtl;
this.disableIndexFieldValidation = !!mongoOptions.disableIndexFieldValidation; this.disableIndexFieldValidation = !!mongoOptions.disableIndexFieldValidation;
this._logClientEvents = mongoOptions.logClientEvents; this._logClientEvents = mongoOptions.logClientEvents;
this._clientMetadata = mongoOptions.clientMetadata;
// Create a copy of mongoOptions and remove Parse Server-specific options that should not // Create a copy of mongoOptions and remove Parse Server-specific options that should not
// be passed to MongoDB client. Note: We only delete from this._mongoOptions, not from the // be passed to MongoDB client. Note: We only delete from this._mongoOptions, not from the
@@ -179,7 +181,17 @@ export class MongoStorageAdapter implements StorageAdapter {
// parsing and re-formatting causes the auth value (if there) to get URI // parsing and re-formatting causes the auth value (if there) to get URI
// encoded // encoded
const encodedUri = formatUrl(parseUrl(this._uri)); const encodedUri = formatUrl(parseUrl(this._uri));
this.connectionPromise = MongoClient.connect(encodedUri, this._mongoOptions)
// Only use driverInfo if clientMetadata option is set
const options = { ...this._mongoOptions };
if (this._clientMetadata) {
options.driverInfo = {
name: this._clientMetadata.name,
version: this._clientMetadata.version
};
}
this.connectionPromise = MongoClient.connect(encodedUri, options)
.then(client => { .then(client => {
// Starting mongoDB 3.0, the MongoClient.connect don't return a DB anymore but a client // Starting mongoDB 3.0, the MongoClient.connect don't return a DB anymore but a client
// Fortunately, we can get back the options and use them to select the proper DB. // Fortunately, we can get back the options and use them to select the proper DB.

View File

@@ -1179,6 +1179,13 @@ module.exports.DatabaseOptions = {
'The MongoDB driver option to specify the amount of time in milliseconds to wait for a connection attempt to finish before trying the next address when using the autoSelectFamily option. If set to a positive integer less than 10, the value 10 is used instead.', 'The MongoDB driver option to specify the amount of time in milliseconds to wait for a connection attempt to finish before trying the next address when using the autoSelectFamily option. If set to a positive integer less than 10, the value 10 is used instead.',
action: parsers.numberParser('autoSelectFamilyAttemptTimeout'), action: parsers.numberParser('autoSelectFamilyAttemptTimeout'),
}, },
clientMetadata: {
env: 'PARSE_SERVER_DATABASE_CLIENT_METADATA',
help:
"Custom metadata to append to database client connections for identifying Parse Server instances in database logs. If set, this metadata will be visible in database logs during connection handshakes. This can help with debugging and monitoring in deployments with multiple database clients. Set `name` to identify your application (e.g., 'MyApp') and `version` to your application's version. Leave undefined (default) to disable this feature and avoid the additional data transfer overhead.",
action: parsers.objectParser,
type: 'DatabaseOptionsClientMetadata',
},
compressors: { compressors: {
env: 'PARSE_SERVER_DATABASE_COMPRESSORS', env: 'PARSE_SERVER_DATABASE_COMPRESSORS',
help: help:
@@ -1461,6 +1468,18 @@ module.exports.DatabaseOptions = {
action: parsers.numberParser('zlibCompressionLevel'), action: parsers.numberParser('zlibCompressionLevel'),
}, },
}; };
module.exports.DatabaseOptionsClientMetadata = {
name: {
env: 'PARSE_SERVER_DATABASE_CLIENT_METADATA_NAME',
help: "The name to identify your application in database logs (e.g., 'MyApp').",
required: true,
},
version: {
env: 'PARSE_SERVER_DATABASE_CLIENT_METADATA_VERSION',
help: "The version of your application (e.g., '1.0.0').",
required: true,
},
};
module.exports.AuthAdapter = { module.exports.AuthAdapter = {
enabled: { enabled: {
help: 'Is `true` if the auth adapter is enabled, `false` otherwise.', help: 'Is `true` if the auth adapter is enabled, `false` otherwise.',

View File

@@ -264,6 +264,7 @@
* @property {String} authSource The MongoDB driver option to specify the database name associated with the user's credentials. * @property {String} authSource The MongoDB driver option to specify the database name associated with the user's credentials.
* @property {Boolean} autoSelectFamily The MongoDB driver option to set whether the socket attempts to connect to IPv6 and IPv4 addresses until a connection is established. If available, the driver will select the first IPv6 address. * @property {Boolean} autoSelectFamily The MongoDB driver option to set whether the socket attempts to connect to IPv6 and IPv4 addresses until a connection is established. If available, the driver will select the first IPv6 address.
* @property {Number} autoSelectFamilyAttemptTimeout The MongoDB driver option to specify the amount of time in milliseconds to wait for a connection attempt to finish before trying the next address when using the autoSelectFamily option. If set to a positive integer less than 10, the value 10 is used instead. * @property {Number} autoSelectFamilyAttemptTimeout The MongoDB driver option to specify the amount of time in milliseconds to wait for a connection attempt to finish before trying the next address when using the autoSelectFamily option. If set to a positive integer less than 10, the value 10 is used instead.
* @property {DatabaseOptionsClientMetadata} clientMetadata Custom metadata to append to database client connections for identifying Parse Server instances in database logs. If set, this metadata will be visible in database logs during connection handshakes. This can help with debugging and monitoring in deployments with multiple database clients. Set `name` to identify your application (e.g., 'MyApp') and `version` to your application's version. Leave undefined (default) to disable this feature and avoid the additional data transfer overhead.
* @property {Union} compressors The MongoDB driver option to specify an array or comma-delimited string of compressors to enable network compression for communication between this client and a mongod/mongos instance. * @property {Union} compressors The MongoDB driver option to specify an array or comma-delimited string of compressors to enable network compression for communication between this client and a mongod/mongos instance.
* @property {Number} connectTimeoutMS The MongoDB driver option to specify the amount of time, in milliseconds, to wait to establish a single TCP socket connection to the server before raising an error. Specifying 0 disables the connection timeout. * @property {Number} connectTimeoutMS The MongoDB driver option to specify the amount of time, in milliseconds, to wait to establish a single TCP socket connection to the server before raising an error. Specifying 0 disables the connection timeout.
* @property {Boolean} createIndexRoleName Set to `true` to automatically create a unique index on the name field of the _Role collection on server start. Set to `false` to skip index creation. Default is `true`.<br><br>⚠️ When setting this option to `false` to manually create the index, keep in mind that the otherwise automatically created index may change in the future to be optimized for the internal usage by Parse Server. * @property {Boolean} createIndexRoleName Set to `true` to automatically create a unique index on the name field of the _Role collection on server start. Set to `false` to skip index creation. Default is `true`.<br><br>⚠️ When setting this option to `false` to manually create the index, keep in mind that the otherwise automatically created index may change in the future to be optimized for the internal usage by Parse Server.
@@ -315,6 +316,12 @@
* @property {Number} zlibCompressionLevel The MongoDB driver option to specify the compression level if using zlib for network compression (0-9). * @property {Number} zlibCompressionLevel The MongoDB driver option to specify the compression level if using zlib for network compression (0-9).
*/ */
/**
* @interface DatabaseOptionsClientMetadata
* @property {String} name The name to identify your application in database logs (e.g., 'MyApp').
* @property {String} version The version of your application (e.g., '1.0.0').
*/
/** /**
* @interface AuthAdapter * @interface AuthAdapter
* @property {Boolean} enabled Is `true` if the auth adapter is enabled, `false` otherwise. * @property {Boolean} enabled Is `true` if the auth adapter is enabled, `false` otherwise.

View File

@@ -755,6 +755,15 @@ export interface DatabaseOptions {
allowPublicExplain: ?boolean; allowPublicExplain: ?boolean;
/* An array of MongoDB client event configurations to enable logging of specific events. */ /* An array of MongoDB client event configurations to enable logging of specific events. */
logClientEvents: ?(LogClientEvent[]); logClientEvents: ?(LogClientEvent[]);
/* Custom metadata to append to database client connections for identifying Parse Server instances in database logs. If set, this metadata will be visible in database logs during connection handshakes. This can help with debugging and monitoring in deployments with multiple database clients. Set `name` to identify your application (e.g., 'MyApp') and `version` to your application's version. Leave undefined (default) to disable this feature and avoid the additional data transfer overhead. */
clientMetadata: ?DatabaseOptionsClientMetadata;
}
export interface DatabaseOptionsClientMetadata {
/* The name to identify your application in database logs (e.g., 'MyApp'). */
name: string;
/* The version of your application (e.g., '1.0.0'). */
version: string;
} }
export interface AuthAdapter { export interface AuthAdapter {

View File

@@ -38,6 +38,7 @@ export const DefaultMongoURI = DefinitionDefaults.databaseURI;
// before passing to MongoDB client // before passing to MongoDB client
export const ParseServerDatabaseOptions = [ export const ParseServerDatabaseOptions = [
'allowPublicExplain', 'allowPublicExplain',
'clientMetadata',
'createIndexRoleName', 'createIndexRoleName',
'createIndexUserEmail', 'createIndexUserEmail',
'createIndexUserEmailCaseInsensitive', 'createIndexUserEmailCaseInsensitive',