Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add logging #4435

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .evergreen/config.in.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,20 @@ functions:
binary: bash
args:
- .evergreen/run-tests.sh
- command: s3.put
params:
aws_key: ${aws_key}
aws_secret: ${aws_secret}
local_file: src/connection-logs.txt
optional: true
# Upload the coverage report for all tasks in a single build to the same directory.
# TODO NODE-4707 - change upload directory to ${UPLOAD_BUCKET}
# This change will require changing the `download and merge coverage` func as well
remote_file: mongo-node-driver/${revision}/${version_id}/${build_variant}/${task_name}/connection-logs.txt
bucket: mciuploads
permissions: public-read
content_type: text/plain
display_name: "Connection Logs"

"run serverless tests":
- command: timeout.update
Expand Down Expand Up @@ -988,6 +1002,20 @@ task_groups:
- .evergreen/setup-serverless.sh

teardown_group:
- command: s3.put
params:
aws_key: ${aws_key}
aws_secret: ${aws_secret}
local_file: src/connection-logs.txt
optional: true
# Upload the coverage report for all tasks in a single build to the same directory.
# TODO NODE-4707 - change upload directory to ${UPLOAD_BUCKET}
# This change will require changing the `download and merge coverage` func as well
remote_file: mongo-node-driver/${revision}/${version_id}/${build_variant}/${task_name}/connection-logs.txt
bucket: mciuploads
permissions: public-read
content_type: text/plain
display_name: "Connection Logs"
- func: "upload test results"
- command: subprocess.exec
params:
Expand Down
22 changes: 22 additions & 0 deletions .evergreen/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,17 @@ functions:
binary: bash
args:
- .evergreen/run-tests.sh
- command: s3.put
params:
aws_key: ${aws_key}
aws_secret: ${aws_secret}
local_file: src/connection-logs.txt
optional: true
remote_file: mongo-node-driver/${revision}/${version_id}/${build_variant}/${task_name}/connection-logs.txt
bucket: mciuploads
permissions: public-read
content_type: text/plain
display_name: Connection Logs
run serverless tests:
- command: timeout.update
params:
Expand Down Expand Up @@ -2874,6 +2885,17 @@ task_groups:
args:
- .evergreen/setup-serverless.sh
teardown_group:
- command: s3.put
params:
aws_key: ${aws_key}
aws_secret: ${aws_secret}
local_file: src/connection-logs.txt
optional: true
remote_file: mongo-node-driver/${revision}/${version_id}/${build_variant}/${task_name}/connection-logs.txt
bucket: mciuploads
permissions: public-read
content_type: text/plain
display_name: Connection Logs
- func: upload test results
- command: subprocess.exec
params:
Expand Down
12 changes: 10 additions & 2 deletions src/cmap/connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,12 @@ export async function makeSocket(options: MakeConnectionOptions): Promise<Stream
socket
.once(connectEvent, () => resolve(socket))
.once('error', cause =>
reject(new MongoNetworkError(MongoError.buildErrorMessage(cause), { cause }))
reject(
new MongoNetworkError(
MongoError.buildErrorMessage(cause) + '- error encountered while establishing socket',
{ cause }
)
)
)
.once('timeout', () => {
reject(
Expand Down Expand Up @@ -489,7 +494,10 @@ async function makeSocks5Connection(options: MakeConnectionOptions): Promise<Str
});
existingSocket = connection.socket;
} catch (cause) {
throw new MongoNetworkError(MongoError.buildErrorMessage(cause), { cause });
throw new MongoNetworkError(
MongoError.buildErrorMessage(cause) + '- during socks5 proxy establishment',
{ cause }
);
}

// Finally, now treat the resulting duplex stream as the
Expand Down
73 changes: 66 additions & 7 deletions src/cmap/connection.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { createWriteStream } from 'fs';
import { type Readable, Transform, type TransformCallback } from 'stream';
import { clearTimeout, setTimeout } from 'timers';

Expand All @@ -6,6 +7,7 @@ import {
deserialize,
type DeserializeOptions,
type Document,
EJSON,
type ObjectId
} from '../bson';
import { type AutoEncrypter } from '../client-side-encryption/auto_encrypter';
Expand Down Expand Up @@ -179,6 +181,45 @@ function streamIdentifier(stream: Stream, options: ConnectionOptions): string {
return uuidV4().toString('hex');
}

export const logger = createWriteStream('connection-logs.txt');
export const write = (payload: Document) => {
payload.timestamp = new Date().toISOString();
payload.hostname = process.env.MONGODB_URI;
payload.ip = process.env.IP;

const log = JSON.stringify(payload);
logger.write(log);
logger.write('\n');
};

const writeEvent =
(event: string) =>
<T extends Document>(payload: T) => {
(payload as T & { event: string }).event = event;
write(payload);
};

export const writeStarted = writeEvent('commandStarted')<{
requestId: number;
connectionId: number | '<monitor>';
}>;

export const readStarted = writeEvent('readStarted')<{
requestId: number;
connectionId: number | '<monitor>';
}>;

export const readSucceeded = writeEvent('readSucceeded')<{
requestId: number;
connectionId: number | '<monitor>';
}>;

export const readFailed = writeEvent('readFailed')<{
requestId: number;
connectionId: number | '<monitor>';
error: Error;
}>;

/** @internal */
export class Connection extends TypedEventEmitter<ConnectionEvents> {
public id: number | '<monitor>';
Expand Down Expand Up @@ -451,7 +492,13 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
this.socketTimeoutMS;
this.socket.setTimeout(timeout);

const payload = {
connectionId: this.id,
requestId: message.requestId
};
try {
writeStarted(payload);

await this.writeCommand(message, {
agreedCompressor: this.description.compressor ?? 'none',
zlibCompressionLevel: this.description.zlibCompressionLevel,
Expand All @@ -476,16 +523,28 @@ export class Connection extends TypedEventEmitter<ConnectionEvents> {
);
}

for await (const response of this.readMany(options)) {
this.socket.setTimeout(0);
const bson = response.parse();
readStarted(payload);
try {
for await (const response of this.readMany(options)) {
readSucceeded(payload);
this.socket.setTimeout(0);
const bson = response.parse();

const document = (responseType ?? MongoDBResponse).make(bson);
const document = (responseType ?? MongoDBResponse).make(bson);

yield document;
this.throwIfAborted();
yield document;

this.socket.setTimeout(timeout);
readStarted(payload);
this.throwIfAborted();

this.socket.setTimeout(timeout);
}
} catch (error) {
readFailed({
...payload,
error
});
throw error;
}
} finally {
this.socket.setTimeout(0);
Expand Down
8 changes: 8 additions & 0 deletions src/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1045,8 +1045,16 @@ export class MongoNetworkError extends MongoError {
* @public
**/
constructor(message: string, options?: MongoNetworkErrorOptions) {
message = message + `(timestamp = ${new Date().toISOString()})`;
super(message, { cause: options?.cause });
this.beforeHandshake = !!options?.beforeHandshake;

const { write } = require('./cmap/connection');
write({
event: 'network error',
message,
error: options?.cause
});
}

override get name(): string {
Expand Down
4 changes: 4 additions & 0 deletions test/tools/runner/hooks/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { NodeVersionFilter } from '../filters/node_version_filter';
import { OSFilter } from '../filters/os_filter';
import { ServerlessFilter } from '../filters/serverless_filter';
import { type Filter } from '../filters/filter';
import { spawnSync } from 'child_process';

// Default our tests to have auth enabled
// A better solution will be tackled in NODE-3714
Expand All @@ -46,6 +47,9 @@ const loadBalanced = SINGLE_MONGOS_LB_URI && MULTI_MONGOS_LB_URI;
const filters: Filter[] = [];

let initializedFilters = false;

process.env.IP = spawnSync('curl', ['ifconfig.me'], { encoding: 'utf-8' }).stdout;

async function initializeFilters(client): Promise<Record<string, any>> {
if (initializedFilters) {
return {};
Expand Down