Adding loggers to TS client

This commit is contained in:
Pawel Kadluczka 2017-08-21 17:51:24 -07:00 committed by Pawel Kadluczka
parent 978f5cebc0
commit e4c1b888ca
9 changed files with 124 additions and 38 deletions

View File

@ -3,6 +3,8 @@ import { IConnection } from "./IConnection"
import { ITransport, TransferMode, TransportType, WebSocketTransport, ServerSentEventsTransport, LongPollingTransport } from "./Transports" import { ITransport, TransferMode, TransportType, WebSocketTransport, ServerSentEventsTransport, LongPollingTransport } from "./Transports"
import { IHttpClient, HttpClient } from "./HttpClient" import { IHttpClient, HttpClient } from "./HttpClient"
import { IHttpConnectionOptions } from "./IHttpConnectionOptions" import { IHttpConnectionOptions } from "./IHttpConnectionOptions"
import { ILogger, LogLevel } from "./ILogger"
import { NullLogger } from "./Loggers"
const enum ConnectionState { const enum ConnectionState {
Initial, Initial,
@ -19,10 +21,11 @@ interface INegotiateResponse {
export class HttpConnection implements IConnection { export class HttpConnection implements IConnection {
private connectionState: ConnectionState; private connectionState: ConnectionState;
private url: string; private url: string;
private connectionId: string; private readonly httpClient: IHttpClient;
private httpClient: IHttpClient; private readonly logger: ILogger;
private readonly options: IHttpConnectionOptions;
private transport: ITransport; private transport: ITransport;
private options: IHttpConnectionOptions; private connectionId: string;
private startPromise: Promise<void>; private startPromise: Promise<void>;
readonly features: any = {}; readonly features: any = {};
@ -30,6 +33,7 @@ export class HttpConnection implements IConnection {
constructor(url: string, options: IHttpConnectionOptions = {}) { constructor(url: string, options: IHttpConnectionOptions = {}) {
this.url = url; this.url = url;
this.httpClient = options.httpClient || new HttpClient(); this.httpClient = options.httpClient || new HttpClient();
this.logger = options.logger || new NullLogger();
this.connectionState = ConnectionState.Initial; this.connectionState = ConnectionState.Initial;
this.options = options; this.options = options;
} }
@ -74,7 +78,7 @@ export class HttpConnection implements IConnection {
this.changeState(ConnectionState.Connecting, ConnectionState.Connected); this.changeState(ConnectionState.Connecting, ConnectionState.Connected);
} }
catch (e) { catch (e) {
console.log("Failed to start the connection. " + e); this.logger.log(LogLevel.Error, "Failed to start the connection. " + e);
this.connectionState = ConnectionState.Disconnected; this.connectionState = ConnectionState.Disconnected;
this.transport = null; this.transport = null;
throw e; throw e;
@ -86,13 +90,13 @@ export class HttpConnection implements IConnection {
transport = TransportType[availableTransports[0]]; transport = TransportType[availableTransports[0]];
} }
if (transport === TransportType.WebSockets && availableTransports.indexOf(TransportType[transport]) >= 0) { if (transport === TransportType.WebSockets && availableTransports.indexOf(TransportType[transport]) >= 0) {
return new WebSocketTransport(); return new WebSocketTransport(this.logger);
} }
if (transport === TransportType.ServerSentEvents && availableTransports.indexOf(TransportType[transport]) >= 0) { if (transport === TransportType.ServerSentEvents && availableTransports.indexOf(TransportType[transport]) >= 0) {
return new ServerSentEventsTransport(this.httpClient); return new ServerSentEventsTransport(this.httpClient, this.logger);
} }
if (transport === TransportType.LongPolling && availableTransports.indexOf(TransportType[transport]) >= 0) { if (transport === TransportType.LongPolling && availableTransports.indexOf(TransportType[transport]) >= 0) {
return new LongPollingTransport(this.httpClient); return new LongPollingTransport(this.httpClient, this.logger);
} }
if (this.isITransport(transport)) { if (this.isITransport(transport)) {

View File

@ -6,21 +6,27 @@ import { IHubProtocol, ProtocolType, MessageType, HubMessage, CompletionMessage,
import { JsonHubProtocol } from "./JsonHubProtocol"; import { JsonHubProtocol } from "./JsonHubProtocol";
import { TextMessageFormat } from "./Formatters" import { TextMessageFormat } from "./Formatters"
import { Base64EncodedHubProtocol } from "./Base64EncodedHubProtocol" import { Base64EncodedHubProtocol } from "./Base64EncodedHubProtocol"
import { ILogger, LogLevel } from "./ILogger"
import { NullLogger } from "./Loggers"
export { TransportType } from "./Transports" export { TransportType } from "./Transports"
export { HttpConnection } from "./HttpConnection" export { HttpConnection } from "./HttpConnection"
export { JsonHubProtocol } from "./JsonHubProtocol" export { JsonHubProtocol } from "./JsonHubProtocol"
export { LogLevel } from "./ILogger"
export { ConsoleLogger } from "./Loggers"
export class HubConnection { export class HubConnection {
private connection: IConnection; private readonly connection: IConnection;
private readonly logger: ILogger;
private protocol: IHubProtocol;
private callbacks: Map<string, (invocationUpdate: CompletionMessage | ResultMessage) => void>; private callbacks: Map<string, (invocationUpdate: CompletionMessage | ResultMessage) => void>;
private methods: Map<string, (...args: any[]) => void>; private methods: Map<string, (...args: any[]) => void>;
private id: number; private id: number;
private connectionClosedCallback: ConnectionClosed; private connectionClosedCallback: ConnectionClosed;
private protocol: IHubProtocol;
constructor(connection: IConnection, protocol: IHubProtocol = new JsonHubProtocol()) { constructor(connection: IConnection, logger: ILogger = new NullLogger(), protocol: IHubProtocol = new JsonHubProtocol()) {
this.connection = connection; this.connection = connection;
this.logger = logger || new NullLogger();
this.protocol = protocol || new JsonHubProtocol(); this.protocol = protocol || new JsonHubProtocol();
this.connection.onDataReceived = data => { this.connection.onDataReceived = data => {
this.onDataReceived(data); this.onDataReceived(data);
@ -57,7 +63,7 @@ export class HubConnection {
} }
break; break;
default: default:
console.log("Invalid message type: " + data); this.logger.log(LogLevel.Warning, "Invalid message type: " + data);
break; break;
} }
} }
@ -72,7 +78,7 @@ export class HubConnection {
} }
} }
else { else {
console.log(`No client method with the name '${invocationMessage.target}' found.`); this.logger.log(LogLevel.Warning, `No client method with the name '${invocationMessage.target}' found.`);
} }
} }

View File

@ -1,7 +1,9 @@
import { IHttpClient } from "./HttpClient" import { IHttpClient } from "./HttpClient"
import { TransportType, ITransport } from "./Transports" import { TransportType, ITransport } from "./Transports"
import { ILogger } from "./ILogger";
export interface IHttpConnectionOptions { export interface IHttpConnectionOptions {
httpClient?: IHttpClient; httpClient?: IHttpClient;
transport?: TransportType | ITransport; transport?: TransportType | ITransport;
logger?: ILogger;
} }

View File

@ -0,0 +1,10 @@
export enum LogLevel {
Information = 0,
Warning,
Error,
None
}
export interface ILogger {
log(logLevel: LogLevel, message: string): void;
}

View File

@ -0,0 +1,20 @@
import { ILogger, LogLevel } from "./ILogger"
export class NullLogger implements ILogger {
log(logLevel: LogLevel, message: string): void {
}
}
export class ConsoleLogger implements ILogger {
private readonly minimumLogLevel: LogLevel;
constructor(minimumLogLevel: LogLevel) {
this.minimumLogLevel = minimumLogLevel;
}
log(logLevel: LogLevel, message: string): void {
if (logLevel >= this.minimumLogLevel) {
console.log(`${LogLevel[logLevel]}: ${message}`);
}
}
}

View File

@ -1,6 +1,7 @@
import { DataReceived, TransportClosed } from "./Common" import { DataReceived, TransportClosed } from "./Common"
import { IHttpClient } from "./HttpClient" import { IHttpClient } from "./HttpClient"
import { HttpError } from "./HttpError" import { HttpError } from "./HttpError"
import { ILogger, LogLevel } from "./ILogger"
export enum TransportType { export enum TransportType {
WebSockets, WebSockets,
@ -22,8 +23,13 @@ export interface ITransport {
} }
export class WebSocketTransport implements ITransport { export class WebSocketTransport implements ITransport {
private readonly logger: ILogger;
private webSocket: WebSocket; private webSocket: WebSocket;
constructor(logger: ILogger) {
this.logger = logger;
}
connect(url: string, requestedTransferMode: TransferMode): Promise<TransferMode> { connect(url: string, requestedTransferMode: TransferMode): Promise<TransferMode> {
return new Promise<TransferMode>((resolve, reject) => { return new Promise<TransferMode>((resolve, reject) => {
@ -35,7 +41,7 @@ export class WebSocketTransport implements ITransport {
} }
webSocket.onopen = (event: Event) => { webSocket.onopen = (event: Event) => {
console.log(`WebSocket connected to ${url}`); this.logger.log(LogLevel.Information, `WebSocket connected to ${url}`);
this.webSocket = webSocket; this.webSocket = webSocket;
resolve(requestedTransferMode); resolve(requestedTransferMode);
}; };
@ -45,7 +51,7 @@ export class WebSocketTransport implements ITransport {
}; };
webSocket.onmessage = (message: MessageEvent) => { webSocket.onmessage = (message: MessageEvent) => {
console.log(`(WebSockets transport) data received: ${message.data}`); this.logger.log(LogLevel.Information, `(WebSockets transport) data received: ${message.data}`);
if (this.onDataReceived) { if (this.onDataReceived) {
this.onDataReceived(message.data); this.onDataReceived(message.data);
} }
@ -86,12 +92,14 @@ export class WebSocketTransport implements ITransport {
} }
export class ServerSentEventsTransport implements ITransport { export class ServerSentEventsTransport implements ITransport {
private readonly httpClient: IHttpClient;
private readonly logger: ILogger;
private eventSource: EventSource; private eventSource: EventSource;
private url: string; private url: string;
private httpClient: IHttpClient;
constructor(httpClient: IHttpClient) { constructor(httpClient: IHttpClient, logger: ILogger) {
this.httpClient = httpClient; this.httpClient = httpClient;
this.logger = logger;
} }
connect(url: string, requestedTransferMode: TransferMode): Promise<TransferMode> { connect(url: string, requestedTransferMode: TransferMode): Promise<TransferMode> {
@ -107,7 +115,7 @@ export class ServerSentEventsTransport implements ITransport {
eventSource.onmessage = (e: MessageEvent) => { eventSource.onmessage = (e: MessageEvent) => {
if (this.onDataReceived) { if (this.onDataReceived) {
try { try {
console.log(`(SSE transport) data received: ${e.data}`); this.logger.log(LogLevel.Information, `(SSE transport) data received: ${e.data}`);
this.onDataReceived(e.data); this.onDataReceived(e.data);
} catch (error) { } catch (error) {
if (this.onClosed) { if (this.onClosed) {
@ -128,7 +136,7 @@ export class ServerSentEventsTransport implements ITransport {
} }
eventSource.onopen = () => { eventSource.onopen = () => {
console.log(`SSE connected to ${this.url}`); this.logger.log(LogLevel.Information, `SSE connected to ${this.url}`);
this.eventSource = eventSource; this.eventSource = eventSource;
// SSE is a text protocol // SSE is a text protocol
resolve(TransferMode.Text); resolve(TransferMode.Text);
@ -156,13 +164,16 @@ export class ServerSentEventsTransport implements ITransport {
} }
export class LongPollingTransport implements ITransport { export class LongPollingTransport implements ITransport {
private readonly httpClient: IHttpClient;
private readonly logger: ILogger;
private url: string; private url: string;
private httpClient: IHttpClient;
private pollXhr: XMLHttpRequest; private pollXhr: XMLHttpRequest;
private shouldPoll: boolean; private shouldPoll: boolean;
constructor(httpClient: IHttpClient) { constructor(httpClient: IHttpClient, logger: ILogger) {
this.httpClient = httpClient; this.httpClient = httpClient;
this.logger = logger;
} }
connect(url: string, requestedTransferMode: TransferMode): Promise<TransferMode> { connect(url: string, requestedTransferMode: TransferMode): Promise<TransferMode> {
@ -187,11 +198,11 @@ export class LongPollingTransport implements ITransport {
if (this.onDataReceived) { if (this.onDataReceived) {
try { try {
if (pollXhr.response) { if (pollXhr.response) {
console.log(`(LongPolling transport) data received: ${pollXhr.response}`); this.logger.log(LogLevel.Information, `(LongPolling transport) data received: ${pollXhr.response}`);
this.onDataReceived(pollXhr.response); this.onDataReceived(pollXhr.response);
} }
else { else {
console.log(`(LongPolling transport) timed out`); this.logger.log(LogLevel.Information, "(LongPolling transport) timed out");
} }
} catch (error) { } catch (error) {
if (this.onClosed) { if (this.onClosed) {

View File

@ -3,4 +3,5 @@ export * from "./HttpConnection"
export * from "./HttpClient" export * from "./HttpClient"
export * from "./HubConnection" export * from "./HubConnection"
export * from "./Transports" export * from "./Transports"
export * from "./Loggers"

View File

@ -29,7 +29,11 @@ describe('connection', () => {
eachTransport(transportType => { eachTransport(transportType => {
it(`over ${signalR.TransportType[transportType]} can send and receive messages`, done => { it(`over ${signalR.TransportType[transportType]} can send and receive messages`, done => {
const message = "Hello World!"; const message = "Hello World!";
let connection = new signalR.HttpConnection(ECHOENDPOINT_URL, { transport: transportType }); let connection = new signalR.HttpConnection(ECHOENDPOINT_URL,
{
transport: transportType,
logger: new signalR.ConsoleLogger(signalR.LogLevel.Information)
});
let received = ""; let received = "";
connection.onDataReceived = data => { connection.onDataReceived = data => {

View File

@ -5,8 +5,12 @@ describe('hubConnection', () => {
describe(`${protocol.name} over ${signalR.TransportType[transportType]} transport`, () => { describe(`${protocol.name} over ${signalR.TransportType[transportType]} transport`, () => {
it(`can invoke server method and receive result`, done => { it(`can invoke server method and receive result`, done => {
const message = "你好,世界!"; const message = "你好,世界!";
let hubConnection = new signalR.HubConnection( let logger = new signalR.ConsoleLogger(signalR.LogLevel.Information);
new signalR.HttpConnection(TESTHUBENDPOINT_URL, { transport: transportType }), protocol); let hubConnection = new signalR.HubConnection(new signalR.HttpConnection(
TESTHUBENDPOINT_URL,
{ transport: transportType, logger: logger }),
logger,
protocol);
hubConnection.onClosed = error => { hubConnection.onClosed = error => {
expect(error).toBe(undefined); expect(error).toBe(undefined);
done(); done();
@ -32,8 +36,13 @@ describe('hubConnection', () => {
}); });
it(`can stream server method and receive result`, done => { it(`can stream server method and receive result`, done => {
let hubConnection = new signalR.HubConnection( let logger = new signalR.ConsoleLogger(signalR.LogLevel.Information);
new signalR.HttpConnection(TESTHUBENDPOINT_URL, { transport: transportType }), protocol); let hubConnection = new signalR.HubConnection(new signalR.HttpConnection(
TESTHUBENDPOINT_URL,
{ transport: transportType, logger: logger }),
logger,
protocol);
hubConnection.onClosed = error => { hubConnection.onClosed = error => {
expect(error).toBe(undefined); expect(error).toBe(undefined);
done(); done();
@ -65,8 +74,13 @@ describe('hubConnection', () => {
it(`rethrows an exception from the server when invoking`, done => { it(`rethrows an exception from the server when invoking`, done => {
const errorMessage = "An error occurred."; const errorMessage = "An error occurred.";
let hubConnection = new signalR.HubConnection(
new signalR.HttpConnection(TESTHUBENDPOINT_URL, { transport: transportType }), protocol); let logger = new signalR.ConsoleLogger(signalR.LogLevel.Information);
let hubConnection = new signalR.HubConnection(new signalR.HttpConnection(
TESTHUBENDPOINT_URL,
{ transport: transportType, logger: logger }),
logger,
protocol);
hubConnection.start() hubConnection.start()
.then(() => { .then(() => {
@ -93,8 +107,13 @@ describe('hubConnection', () => {
it(`rethrows an exception from the server when streaming`, done => { it(`rethrows an exception from the server when streaming`, done => {
const errorMessage = "An error occurred."; const errorMessage = "An error occurred.";
let hubConnection = new signalR.HubConnection(
new signalR.HttpConnection(TESTHUBENDPOINT_URL, { transport: transportType }), protocol); let logger = new signalR.ConsoleLogger(signalR.LogLevel.Information);
let hubConnection = new signalR.HubConnection(new signalR.HttpConnection(
TESTHUBENDPOINT_URL,
{ transport: transportType, logger: logger }),
logger,
protocol);
hubConnection.start() hubConnection.start()
.then(() => { .then(() => {
@ -120,20 +139,25 @@ describe('hubConnection', () => {
}); });
it(`can receive server calls`, done => { it(`can receive server calls`, done => {
let client = new signalR.HubConnection( let logger = new signalR.ConsoleLogger(signalR.LogLevel.Information);
new signalR.HttpConnection(TESTHUBENDPOINT_URL, { transport: transportType }), protocol); let hubConnection = new signalR.HubConnection(new signalR.HttpConnection(
TESTHUBENDPOINT_URL,
{ transport: transportType, logger: logger }),
logger,
protocol);
const message = "你好 SignalR"; const message = "你好 SignalR";
let callbackPromise = new Promise((resolve, reject) => { let callbackPromise = new Promise((resolve, reject) => {
client.on("Message", msg => { hubConnection.on("Message", msg => {
expect(msg).toBe(message); expect(msg).toBe(message);
resolve(); resolve();
}); });
}); });
client.start() hubConnection.start()
.then(() => { .then(() => {
return Promise.all([client.invoke('InvokeWithString', message), callbackPromise]); return Promise.all([hubConnection.invoke('InvokeWithString', message), callbackPromise]);
}) })
.then(() => { .then(() => {
return stop(); return stop();
@ -154,8 +178,12 @@ describe('hubConnection', () => {
ServerSentEvents: "Error occurred" ServerSentEvents: "Error occurred"
}; };
let hubConnection = new signalR.HubConnection( let logger = new signalR.ConsoleLogger(signalR.LogLevel.Information);
new signalR.HttpConnection(`http://${document.location.host}/uncreatable`, { transport: transportType }), protocol); let hubConnection = new signalR.HubConnection(new signalR.HttpConnection(
`http://${document.location.host}/uncreatable`,
{ transport: transportType, logger: logger }),
logger,
protocol);
hubConnection.onClosed = error => { hubConnection.onClosed = error => {
expect(error.message).toMatch(errorRegex[signalR.TransportType[transportType]]); expect(error.message).toMatch(errorRegex[signalR.TransportType[transportType]]);