Skip to content
Merged
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
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,19 @@ The following shows several events that allow for detailed logging and inspectio
> The logging can be useful for analysis and instrumentation of production apps
> where no console access is possible

#### Runtime verbose logging

The `logger`, `isVerbose`, and `isPrivate` options passed to `Meteor.connect`
can also be updated after connecting:

```js
Meteor.setLoggingOptions({
isVerbose: true,
isPrivate: false,
logger: (message) => console.debug(message),
});
```

#### Data level events (high level)

The most convenient way to track internals is via `Data.onChange`:
Expand Down
48 changes: 33 additions & 15 deletions lib/ddp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ interface DDPOptions {
deferReplayUntilLogin?: boolean;
}

type DDPLoggingOptions = Pick<DDPOptions, 'logger' | 'isPrivate' | 'isVerbose'>;

/**
* The default timout in ms until a reconnection attempt starts
* @type {number}
Expand Down Expand Up @@ -260,21 +262,20 @@ class DDP extends EventEmitter<DDPEventMap> {

this.socket = new Socket(options.SocketConstructor, options.endpoint);

if (this.isVerbose) {
this.socket.on('message:out', (outMessage) => {
try {
const { params, ...rest } = outMessage as any;
const base = { SEND: 'SEND', ...rest };
this.logger(
this.isPrivate && params !== undefined
? base
: { SEND: 'SEND', ...outMessage }
);
} catch (e) {
// no-op
}
});
}
this.socket.on('message:out', (outMessage) => {
if (!this.isVerbose) return;
try {
const { params, ...rest } = outMessage as any;
const base = { SEND: 'SEND', ...rest };
this.logger(
this.isPrivate && params !== undefined
? base
: { SEND: 'SEND', ...outMessage }
);
} catch (e) {
// no-op
}
});

this.socket.on('open', () => {
this.isVerbose &&
Expand Down Expand Up @@ -455,6 +456,23 @@ class DDP extends EventEmitter<DDPEventMap> {
this.socket.close();
}

setLoggingOptions(options: DDPLoggingOptions): void {
if (options.logger !== undefined) {
this.logger = options.logger;
}
if (options.isPrivate !== undefined) {
this.isPrivate = options.isPrivate;
}
if (options.isVerbose !== undefined) {
this.isVerbose = options.isVerbose;
}

this.messageQueue.setLoggingOptions({
logger: this.logger,
isVerbose: this.isVerbose,
});
}

/**
* Pushes a method to the message queue.
* This is what happens under the hood when using {Meteor.call}
Expand Down
9 changes: 9 additions & 0 deletions lib/queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@ export default class Queue<T extends object = any> {
this.isVerbose = options.isVerbose ?? false;
}

setLoggingOptions(options: QueueOptions): void {
if (options.logger !== undefined) {
this.logger = options.logger;
}
if (options.isVerbose !== undefined) {
this.isVerbose = options.isVerbose;
}
}

/**
* Adds a new element to the queue
* @param element {any} likely an object
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@meteorrn/core",
"version": "2.28.2",
"version": "2.29.0",
"description": "Meteor Client for React Native",
"type": "module",
"main": "dist/src/index.js",
Expand Down
24 changes: 23 additions & 1 deletion src/Meteor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import EJSON from 'ejson';
import DDP from '../lib/ddp';
import Random from '../lib/Random';

import Data, { type LoggerPayload } from './Data';
import Data, { type ConnectOptions, type LoggerPayload } from './Data';
import Mongo from './Mongo';
import { Collection, getObservers, localCollections } from './Collection';
import call from './Call';
Expand All @@ -20,6 +20,11 @@ type DdpErrorPayload = {
details?: any;
};

export type MeteorLoggingOptions = Pick<
ConnectOptions,
'isVerbose' | 'isPrivate' | 'logger'
>;

function toMeteorStyleError(
payload?: DdpErrorPayload | null
): Error | undefined {
Expand Down Expand Up @@ -55,6 +60,7 @@ export interface MeteorBase {
waitDdpConnected: (cb: (...args: any[]) => void) => void;
reconnect(): void;
connect(endpoint?: string, options?: any): void;
setLoggingOptions(options: MeteorLoggingOptions): void;
requireDdp(): DDP;
subscribe(
name: string,
Expand Down Expand Up @@ -166,6 +172,22 @@ const Meteor: MeteorBase = {
reconnect() {
Data.ddp && Data.ddp.connect();
},
setLoggingOptions(options: MeteorLoggingOptions) {
if (options.isVerbose !== undefined) {
this.isVerbose = options.isVerbose;
}
if (options.logger !== undefined) {
this.logger = options.logger;
}

Data._options = {
...Data._options,
...Object.fromEntries(
Object.entries(options).filter(([, value]) => value !== undefined)
),
};
Data.ddp?.setLoggingOptions(options);
},
/**
* Connect to a Meteor server using a given websocket endpoint.
* The endpoint needs to start with `ws://` or `wss://`
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ const { useTracker, withTracker, Mongo, ReactiveDict } = MeteorAugmented;

export { useTracker, Accounts, withTracker, Mongo, ReactiveDict, Tracker };
export { Vent } from './vent';
export type { MeteorLoggingOptions } from './Meteor';
export type { LoginFailurePayload } from './user/User';
export default MeteorAugmented;
43 changes: 43 additions & 0 deletions test/lib/ddp.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -292,4 +292,47 @@ describe('ddp', function () {
});
});
});
describe('logging', function () {
it('updates logging options at runtime', function (done) {
const logs = [];
validOptions.autoConnect = false;
validOptions.autoReconnect = false;
validOptions.isVerbose = false;
validOptions.isPrivate = true;
validOptions.logger = (message) => logs.push(message);

ddp = new DDP(validOptions);
ddp.setLoggingOptions({
isVerbose: true,
isPrivate: false,
});

listen(ddp.socket, 'open', () => {
try {
ddp.socket.emit('message:in', {
msg: 'connected',
session: 'session',
});
ddp.method('foo', { foo: 'bar' });

const sentMethod = logs.find(
(message) => message.SEND === 'SEND' && message.msg === 'method'
);
const queuedMethod = logs.find(
(message) => message.QUEUE === 'ENQUEUE' && message.msg === 'method'
);

expect(sentMethod).to.exist;
expect(queuedMethod).to.exist;
expect(sentMethod.params).to.deep.equal({ foo: 'bar' });
expect(queuedMethod.params).to.equal(undefined);
done();
} catch (error) {
done(error);
}
});

ddp.connect();
});
});
});
42 changes: 42 additions & 0 deletions test/src/Meteor.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,46 @@ describe('Meteor - integration', function () {
});
});
});

describe(Meteor.setLoggingOptions.name, () => {
it('updates Meteor and current DDP logging options', () => {
const data = Meteor.getData();
const previousDdp = data.ddp;
const previousOptions = data._options;
const previousIsVerbose = Meteor.isVerbose;
const previousLogger = Meteor.logger;
const logger = () => {};
const calls = [];

data.ddp = {
setLoggingOptions: (options) => calls.push(options),
};

try {
Meteor.setLoggingOptions({
isVerbose: true,
isPrivate: false,
logger,
});

expect(Meteor.isVerbose).to.equal(true);
expect(Meteor.logger).to.equal(logger);
expect(data._options.isVerbose).to.equal(true);
expect(data._options.isPrivate).to.equal(false);
expect(data._options.logger).to.equal(logger);
expect(calls).to.deep.equal([
{
isVerbose: true,
isPrivate: false,
logger,
},
]);
} finally {
data.ddp = previousDdp;
data._options = previousOptions;
Meteor.isVerbose = previousIsVerbose;
Meteor.logger = previousLogger;
}
});
});
});
Loading