Skip to content
Closed
Show file tree
Hide file tree
Changes from 4 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
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
* Added utility functions `isStandardContextType(contextType: string)`, `isStandardIntent(intent: string)`,`getPossibleContextsForIntent(intent: StandardIntent)`. ([#1139](https://github.com/finos/FDC3/pull/1139))
* Added support for event listening outside of intent or context listnener. Added new function `addEventListener`, type `EventHandler`, enum `FDC3EventType` and interfaces `FDC3Event` and `FDC3ChannelChangedEvent`. ([#1207](https://github.com/finos/FDC3/pull/1207))
* Added new `CreateOrUpdateProfile` intent. ([#1359](https://github.com/finos/FDC3/pull/1359))
* Added separate `fdc3-commonjs` module for compatibility with older projects that use CommonJS. ([#1452](https://github.com/finos/FDC3/pull/1452))
* Added separate `fdc3-commonjs` module for compatibility with older projects that use CommonJS. ([#1452](https://github.com/finos/FDC3/pull/1452))
* Added the ability to control logging to the JS console from getAgent() and the DesktopAgentProxy via arguments to getAgent(). ([#1495](https://github.com/finos/FDC3/pull/1495))

### Changed

Expand Down
13 changes: 11 additions & 2 deletions packages/fdc3-agent-proxy/src/DesktopAgentProxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ import { IntentSupport } from './intents/IntentSupport';
import { Connectable, Channel } from '@finos/fdc3-standard';
import { Context } from '@finos/fdc3-context';
import { HeartbeatSupport } from './heartbeat/HeartbeatSupport';
import { Logger } from './util/Logger';

export type DesktopAgentProxyLogSettings = {
heartbeat: boolean;
debug: boolean;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be explicit level for more refined control versus just debug yes or no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its does feel a little scrappy this way, but the issue is we've got two scopes to control logging in: the connection phase and the proxy itself + within the proxy there are two types of debug message that we need to keep separate for usability: logging API messages sent/received and logging heartbeat messages. Collectively, that made it a little harder to base purely on a log level.

I suppose one solution would be to move the message logging in the proxy to the log level but set the default level to warn...

The current proposed args are:

export type GetAgentLogSettings = {
  connection: boolean,  //default true
  connectionDebug: boolean,  //default false
  proxyDebug: boolean,  //default false
  heartbeat: boolean  // default false
};

By moving message loggimg to the log level we could do:

export type GetAgentLogSettings = {
  connectionLogLevel: "debug" | "log" | "warn" | "error" ,  //default "log"
  proxyLogLevel: boolean,  "debug" | "log" | "warn" | "error" ,  //default "warn"
};

};

/**
* This splits out the functionality of the desktop agent into
Expand All @@ -33,21 +39,24 @@ export class DesktopAgentProxy implements DesktopAgent, Connectable {
channels: ChannelSupport,
intents: IntentSupport,
apps: AppSupport,
connectables: Connectable[]
connectables: Connectable[],
logging: DesktopAgentProxyLogSettings
) {
this.heartbeat = heartbeat;
this.intents = intents;
this.channels = channels;
this.apps = apps;
this.connectables = connectables;
Logger.enableHeartbeatLogs(logging.heartbeat);
Logger.enableDebugLogs(logging.debug);
}

addEventListener(type: FDC3EventTypes | null, handler: EventHandler): Promise<Listener> {
switch (type) {
case 'userChannelChanged':
return this.channels.addChannelChangedEventHandler(handler);
default:
console.warn(`Tried to add a listener for an unknown event type: ${type}`);
Logger.warn(`Tried to add a listener for an unknown event type: ${type}`);
return Promise.reject(new Error('UnknownEventType'));
}
}
Expand Down
5 changes: 3 additions & 2 deletions packages/fdc3-agent-proxy/src/apps/DefaultAppSupport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import {
OpenRequest,
OpenResponse,
} from '@finos/fdc3-schema/generated/api/BrowserTypes';
import { throwIfUndefined } from '../util';
import { throwIfUndefined } from '../util/throwIfUndefined';
import { Logger } from '../util/Logger';
export class DefaultAppSupport implements AppSupport {
readonly messaging: Messaging;

Expand Down Expand Up @@ -96,7 +97,7 @@ export class DefaultAppSupport implements AppSupport {
return response.payload.implementationMetadata;
} else {
//This will only happen if the DA implementation returns an invalid message with a missing implementationMetadata property
console.error('Invalid response from Desktop Agent to open!', response);
Logger.error('Invalid response from Desktop Agent to open!', response);
const unknownImpl: ImplementationMetadata = {
fdc3Version: 'unknown',
provider: 'unknown',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ import {
JoinUserChannelResponse,
JoinUserChannelRequest,
} from '@finos/fdc3-schema/generated/api/BrowserTypes';
import { throwIfUndefined } from '../util';
import { throwIfUndefined } from '../util/throwIfUndefined';
import { Logger } from '../util/Logger';

export class DefaultChannelSupport implements ChannelSupport {
readonly messaging: Messaging;
Expand All @@ -40,7 +41,7 @@ export class DefaultChannelSupport implements ChannelSupport {
this.messaging = messaging;
this.channelSelector = channelSelector;
this.channelSelector.setChannelChangeCallback((channelId: string | null) => {
console.debug('Channel selector reports channel changed: ' + channelId);
Logger.debug('Channel selector reports channel changed: ', channelId);
if (channelId == null) {
this.leaveUserChannel();
} else {
Expand All @@ -49,6 +50,7 @@ export class DefaultChannelSupport implements ChannelSupport {
});

this.addChannelChangedEventHandler(e => {
Logger.debug('Desktop Agent reports channel changed: ', e.details.newChannelId);
this.channelSelector.updateChannel(e.details.newChannelId, this.userChannels);
});
}
Expand Down Expand Up @@ -177,7 +179,6 @@ export class DefaultChannelSupport implements ChannelSupport {
}

async addContextListener(handler: ContextHandler, type: string | null): Promise<Listener> {
//TODO: Figure out a better solution than inlining this class.
/** Utility class used to wrap the DefaultContextListener and ensure it gets removed
* when its unsubscribe function is called.
*/
Expand Down
2 changes: 2 additions & 0 deletions packages/fdc3-agent-proxy/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { DefaultAppSupport } from './apps/DefaultAppSupport';
import { AppSupport } from './apps/AppSupport';
import { DefaultHeartbeatSupport } from './heartbeat/DefaultHeartbeatSupport';
import { Connectable } from '@finos/fdc3-standard';
import { AbstractFDC3Logger } from './util/AbstractFDC3Logger';

export {
type Messaging,
Expand All @@ -26,4 +27,5 @@ export {
DefaultHeartbeatSupport,
RegisterableListener,
Connectable,
AbstractFDC3Logger,
};
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
RaiseIntentResponse,
RaiseIntentResultResponse,
} from '@finos/fdc3-schema/generated/api/BrowserTypes';
import { throwIfUndefined } from '../util';
import { throwIfUndefined } from '../util/throwIfUndefined';

const convertIntentResult = async (
{ payload }: RaiseIntentResultResponse,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
} from '@finos/fdc3-schema/generated/api/BrowserTypes';
import { Messaging } from '../Messaging';
import { RegisterableListener } from './RegisterableListener';
import { throwIfUndefined } from '../util';
import { throwIfUndefined } from '../util/throwIfUndefined';
import { ChannelError } from '@finos/fdc3-standard';

type SubscriptionRequest =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
} from '@finos/fdc3-schema/generated/api/BrowserTypes';
import { Messaging } from '../Messaging';
import { RegisterableListener } from './RegisterableListener';
import { Logger } from '../util/Logger';

export class HeartbeatListener implements RegisterableListener {
readonly id: string;
Expand All @@ -20,6 +21,7 @@ export class HeartbeatListener implements RegisterableListener {
}

action(_m: AgentEventMessage): void {
Logger.heartbeatLog('Responding to heartbeat request', _m);
const request: HeartbeatAcknowledgementRequest = {
type: 'heartbeatAcknowledgementRequest',
meta: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
PrivateChannelUnsubscribeEvent,
} from '@finos/fdc3-standard';
import { BrowserTypes } from '@finos/fdc3-schema';
import { Logger } from '../util/Logger';
const {
isPrivateChannelOnAddContextListenerEvent,
isPrivateChannelOnDisconnectEvent,
Expand Down Expand Up @@ -116,7 +117,7 @@ export class PrivateChannelDisconnectEventListener extends AbstractPrivateChanne
};
handler(event);
} else {
console.error('PrivateChannelDisconnectEventListener was called for a different message type!', msg);
Logger.error('PrivateChannelDisconnectEventListener was called for a different message type!', msg);
}
};

Expand All @@ -134,7 +135,7 @@ export class PrivateChannelAddContextEventListener extends AbstractPrivateChanne
};
handler(event);
} else {
console.error('PrivateChannelAddContextEventListener was called for a different message type!', msg);
Logger.error('PrivateChannelAddContextEventListener was called for a different message type!', msg);
}
};
super(messaging, channelId, ['privateChannelOnAddContextListenerEvent'], 'addContextListener', wrappedHandler);
Expand All @@ -151,7 +152,7 @@ export class PrivateChannelUnsubscribeEventListener extends AbstractPrivateChann
};
handler(event);
} else {
console.error('PrivateChannelUnsubscribeEventListener was called for a different message type!', msg);
Logger.error('PrivateChannelUnsubscribeEventListener was called for a different message type!', msg);
}
};
super(messaging, channelId, ['privateChannelOnUnsubscribeEvent'], 'unsubscribe', wrappedHandler);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
AppRequestMessage,
WebConnectionProtocol6Goodbye,
} from '@finos/fdc3-schema/generated/api/BrowserTypes';
import { Logger } from '../util/Logger';

export abstract class AbstractMessaging implements Messaging {
private appIdentifier: AppIdentifier;
Expand All @@ -30,6 +31,7 @@ export abstract class AbstractMessaging implements Messaging {
id,
filter: filter,
action: m => {
Logger.debug('Received from DesktopAgent: ', m);
done = true;
this.unregister(id);
if (timeout) {
Expand All @@ -51,7 +53,7 @@ export abstract class AbstractMessaging implements Messaging {
timeout = setTimeout(() => {
this.unregister(id);
if (!done) {
console.error(
Logger.error(
`waitFor rejecting after ${this.getTimeoutMs()}ms at ${new Date().toISOString()} with ${timeoutErrorMessage}`
);
reject(new Error(timeoutErrorMessage));
Expand All @@ -71,6 +73,7 @@ export abstract class AbstractMessaging implements Messaging {
const prom = this.waitFor<X>(m => {
return m.type == expectedTypeName && m.meta.requestUuid == message.meta.requestUuid;
}, errorMessage);
Logger.debug('Sending to DesktopAgent: ', message);
this.post(message);
const out: X = await prom;
if (out?.payload?.error) {
Expand Down
89 changes: 89 additions & 0 deletions packages/fdc3-agent-proxy/src/util/AbstractFDC3Logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/* eslint-disable @typescript-eslint/no-explicit-any */

//check if color is supported in (node) console;
let noColor = true;
//else only occurs in a browser and can't be tested in node
/* istanbul ignore if */
if (typeof process !== 'undefined') {
const argv = process.argv || /* istanbul ignore next */ [];
const env = process.env || /* istanbul ignore next */ {};
noColor =
(!!env.NO_COLOR || argv.includes('--no-color')) &&
!(
!!env.FORCE_COLOR ||
argv.includes('--color') ||
process.platform === 'win32' /* istanbul ignore next */ ||
((process.stdout || {}).isTTY && env.TERM !== 'dumb') ||
/* istanbul ignore next */ !!env.CI
);
}

type ColorFn = (aString: string) => string;

export abstract class AbstractFDC3Logger {
private static enableDebug: boolean = false;
private static enableLog: boolean = true;

/** This should be overridden by sub-classes to change the prefix applied
* to log messages. */
/* istanbul ignore next */ static get prefix(): string {
return '';
}

public static enableDebugLogs(enable: boolean) {
this.enableDebug = enable;
}

public static enableLogs(enable: boolean) {
this.enableLog = enable;
}

protected static debugColor(value: string): string {
return noColor ? /* istanbul ignore next */ value : '\x1b[30m\x1b[2m' + value + '\x1b[22m\x1b[39m';
}
protected static logColor(value: string): string {
return noColor ? /* istanbul ignore next */ value : '\x1b[32m\x1b[2m' + value + '\x1b[22m\x1b[39m';
}
protected static warnColor(value: string): string {
return noColor ? /* istanbul ignore next */ value : '\x1b[33m' + value + '\x1b[39m';
}
protected static errorColor(value: string): string {
return noColor ? /* istanbul ignore next */ value : '\x1b[31m' + value + '\x1b[39m';
}

public static debug(...params: any[]) {
if (this.enableDebug) {
console.debug(...this.prefixAndColorize(this.prefix, params, this.debugColor));
}
}

public static log(...params: any[]) {
if (this.enableLog) {
console.log(...this.prefixAndColorize(this.prefix, params, this.logColor));
}
}

public static warn(...params: any[]) {
console.warn(...this.prefixAndColorize(this.prefix, params, this.warnColor));
}

public static error(...params: any[]) {
console.error(...this.prefixAndColorize(this.prefix, params, this.errorColor));
}

protected static prefixAndColorize = (prefix: string, params: any[], colorFn: ColorFn): string[] => {
const prefixed = [prefix, ...params];
return prefixed.map(value => {
if (typeof value === 'string') {
//just color strings
return colorFn(value);
} else if (value && value.stack && value.message) {
//probably an error
return colorFn(value.stack);
} else {
//something else... lets hope it stringifies
return colorFn(JSON.stringify(value, null, 2));
}
});
};
}
19 changes: 19 additions & 0 deletions packages/fdc3-agent-proxy/src/util/Logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { AbstractFDC3Logger } from './AbstractFDC3Logger';

export class Logger extends AbstractFDC3Logger {
static override get prefix(): string {
return 'FDC3 DesktopAgentProxy: ';
}

private static enableHeartbeatLog: boolean = true;

public static enableHeartbeatLogs(enable: boolean) {
this.enableHeartbeatLog = enable;
}

public static heartbeatLog(...params: any[]) {
if (this.enableHeartbeatLog) {
console.debug(...this.prefixAndColorize(this.prefix, params, this.debugColor));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { AgentEventMessage, AgentResponseMessage } from '@finos/fdc3-schema/generated/api/BrowserTypes';
import { ChannelError, OpenError, ResolveError } from '@finos/fdc3-standard';
import { Logger } from './Logger';

export type ErrorMessages = ChannelError | OpenError | ResolveError;

Expand All @@ -14,7 +15,7 @@ export const throwIfUndefined = (
absentError: ErrorMessages
): void => {
if (property === undefined) {
console.error(absentMessage, '\nDACP message that resulted in the undefined property: ', message);
Logger.error(absentMessage, '\nDACP message that resulted in the undefined property: ', message);
throw new Error(absentError);
}
};
4 changes: 4 additions & 0 deletions packages/fdc3-agent-proxy/test/features/utils.feature
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@ Feature: Utility functions
Scenario: throwIfUndefined is used to check properties
When I call throwIfUndefined it throws if a specified property is not defined
And I call throwIfUndefined it does NOT throw if a specified property IS defined

Scenario: Logger utility
When All log functions are used with a message
When All log functions are used with an error

Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Given(
const is = new DefaultIntentSupport(this.messaging, new SimpleIntentResolver(this));
const as = new DefaultAppSupport(this.messaging);

const da = new DesktopAgentProxy(hs, cs, is, as, [hs]);
const da = new DesktopAgentProxy(hs, cs, is, as, [hs], { debug: true, heartbeat: false });
await da.connect();

this.props[daField] = da;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Given('A Desktop Agent in {string}', async function (this: CustomWorld, field: s
const is = new DefaultIntentSupport(this.messaging, new SimpleIntentResolver(this));
const as = new DefaultAppSupport(this.messaging);

const da = new DesktopAgentProxy(hs, cs, is, as, [hs]);
const da = new DesktopAgentProxy(hs, cs, is, as, [hs], { debug: false, heartbeat: false });
await da.connect();

this.props[field] = da;
Expand Down
Loading
Loading