Skip to content

Commit 067ce23

Browse files
authored
Add client side logging (#72)
* Initial commit Signed-off-by: nithinkdb <nithin.krishnamurthi@databricks.com> * Fixed comments ran linter Signed-off-by: nithinkdb <nithin.krishnamurthi@databricks.com> * Changed logging of uuids Signed-off-by: nithinkdb <nithin.krishnamurthi@databricks.com> * Fixed up comments, added functionality Signed-off-by: nithinkdb <nithin.krishnamurthi@databricks.com> * Fixed bugs Signed-off-by: nithinkdb <nithin.krishnamurthi@databricks.com> * Fixed import Signed-off-by: nithinkdb <nithin.krishnamurthi@databricks.com> * Ran linter Signed-off-by: nithinkdb <nithin.krishnamurthi@databricks.com> * Fixed up comments Signed-off-by: nithinkdb <nithin.krishnamurthi@databricks.com> * Added in substitution I'd forgotten Signed-off-by: nithinkdb <nithin.krishnamurthi@databricks.com> * Fixed code style Signed-off-by: nithinkdb <nithin.krishnamurthi@databricks.com> * Changed comment Signed-off-by: nithinkdb <nithin.krishnamurthi@databricks.com> * Ran Linter Signed-off-by: nithinkdb <nithin.krishnamurthi@databricks.com> Signed-off-by: nithinkdb <nithin.krishnamurthi@databricks.com>
1 parent 9ec1be1 commit 067ce23

13 files changed

Lines changed: 664 additions & 81 deletions

File tree

.eslintrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
"consistent-return": "off",
1313
"no-param-reassign": "off",
1414
"no-bitwise": "off",
15-
"@typescript-eslint/no-throw-literal": "off"
15+
"@typescript-eslint/no-throw-literal": "off",
16+
"no-restricted-syntax": "off"
1617
}
1718
}
1819
]

examples/logging.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
const { LOADIPHLPAPI } = require('dns');
2+
const { DBSQLClient, DBSQLLogger, LogLevel } = require('../');
3+
4+
// This logger will emit logs to console and log.txt
5+
//
6+
const logger = new DBSQLLogger('log.txt', LogLevel.info);
7+
8+
const client = new DBSQLClient({ logger: logger });
9+
10+
client
11+
.connect({
12+
host: 'eng-academy.staging.cloud.databricks.com',
13+
path: '/sql/1.0/endpoints/410886e8ec4b0796',
14+
token: 'dapi5aef9f11ea0f3003440555526226ef7f',
15+
})
16+
.then(async (client) => {
17+
const session = await client.openSession();
18+
19+
let queryOperation = await session.executeStatement('SELECT "Hello, World!"', { runAsync: true });
20+
let result = await queryOperation.fetchAll();
21+
await queryOperation.close();
22+
23+
console.table(result);
24+
25+
// Set logger to different level.
26+
//
27+
logger.setLevel(LogLevel.debug);
28+
29+
queryOperation = await session.executeStatement('SELECT "Hello, World!"', { runAsync: true });
30+
result = await queryOperation.fetchAll();
31+
await queryOperation.close();
32+
33+
await session.close();
34+
await client.close();
35+
})
36+
.catch((error) => {
37+
console.log(error);
38+
});

lib/DBSQLClient.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import thrift from 'thrift';
33
import { EventEmitter } from 'events';
44
import TCLIService from '../thrift/TCLIService';
55
import { TProtocolVersion } from '../thrift/TCLIService_types';
6-
import IDBSQLClient, { ConnectionOptions, OpenSessionRequest } from './contracts/IDBSQLClient';
6+
import IDBSQLClient, { ConnectionOptions, OpenSessionRequest, ClientOptions } from './contracts/IDBSQLClient';
77
import HiveDriver from './hive/HiveDriver';
88
import { Int64 } from './hive/Types';
99
import DBSQLSession from './DBSQLSession';
@@ -18,6 +18,8 @@ import StatusFactory from './factory/StatusFactory';
1818
import HiveDriverError from './errors/HiveDriverError';
1919
import { buildUserAgentString, definedOrError } from './utils';
2020
import PlainHttpAuthentication from './connection/auth/PlainHttpAuthentication';
21+
import IDBSQLLogger, { LogLevel } from './contracts/IDBSQLLogger';
22+
import DBSQLLogger from './DBSQLLogger';
2123

2224
function getInitialNamespaceOptions(catalogName?: string, schemaName?: string) {
2325
if (!catalogName && !schemaName) {
@@ -43,15 +45,19 @@ export default class DBSQLClient extends EventEmitter implements IDBSQLClient {
4345

4446
private authProvider: IAuthentication;
4547

48+
private logger: IDBSQLLogger;
49+
4650
private thrift = thrift;
4751

48-
constructor() {
52+
constructor(options: ClientOptions) {
4953
super();
5054
this.connectionProvider = new HttpConnection();
5155
this.authProvider = new NoSaslAuthentication();
5256
this.statusFactory = new StatusFactory();
57+
this.logger = options?.logger || new DBSQLLogger();
5358
this.client = null;
5459
this.connection = null;
60+
this.logger.log(LogLevel.info, 'Created DBSQLClient');
5561
}
5662

5763
private getConnectionOptions(options: ConnectionOptions): IConnectionOptions {
@@ -88,18 +94,22 @@ export default class DBSQLClient extends EventEmitter implements IDBSQLClient {
8894
this.client = this.thrift.createClient(TCLIService, this.connection.getConnection());
8995

9096
this.connection.getConnection().on('error', (error: Error) => {
97+
this.logger.log(LogLevel.error, JSON.stringify(error));
9198
this.emit('error', error);
9299
});
93100

94101
this.connection.getConnection().on('reconnecting', (params: { delay: number; attempt: number }) => {
102+
this.logger.log(LogLevel.debug, `Reconnecting, params: ${JSON.stringify(params)}`);
95103
this.emit('reconnecting', params);
96104
});
97105

98106
this.connection.getConnection().on('close', () => {
107+
this.logger.log(LogLevel.debug, 'Closing connection.');
99108
this.emit('close');
100109
});
101110

102111
this.connection.getConnection().on('timeout', () => {
112+
this.logger.log(LogLevel.debug, 'Connection timed out.');
103113
this.emit('timeout');
104114
});
105115

@@ -129,7 +139,7 @@ export default class DBSQLClient extends EventEmitter implements IDBSQLClient {
129139
})
130140
.then((response) => {
131141
this.statusFactory.create(response.status);
132-
return new DBSQLSession(driver, definedOrError(response.sessionHandle));
142+
return new DBSQLSession(driver, definedOrError(response.sessionHandle), this.logger);
133143
});
134144
}
135145

lib/DBSQLLogger.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import winston, { Logger } from 'winston';
2+
import IDBSQLLogger, { LogLevel } from './contracts/IDBSQLLogger';
3+
4+
export default class DBSQLLogger implements IDBSQLLogger {
5+
logger: Logger;
6+
7+
transports: any;
8+
9+
constructor(filepath?: string, level = LogLevel.info) {
10+
this.transports = {
11+
console: new winston.transports.Console({ handleExceptions: true, level }),
12+
};
13+
this.logger = winston.createLogger({
14+
transports: [this.transports.console],
15+
});
16+
if (filepath) {
17+
this.transports.file = new winston.transports.File({ filename: filepath, handleExceptions: true, level });
18+
this.logger.add(this.transports.file);
19+
}
20+
}
21+
22+
async log(level: LogLevel, message: string) {
23+
this.logger.log({ level, message });
24+
}
25+
26+
setLevel(level: LogLevel) {
27+
for (const key of Object.keys(this.transports)) {
28+
this.transports[key].level = level;
29+
}
30+
}
31+
}

lib/DBSQLOperation/index.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { stringify, NIL, parse } from 'uuid';
12
import IOperation, { FetchOptions, GetSchemaOptions, FinishedOptions, defaultMaxRows } from '../contracts/IOperation';
23
import HiveDriver from '../hive/HiveDriver';
34
import {
@@ -13,12 +14,15 @@ import OperationStatusHelper from './OperationStatusHelper';
1314
import SchemaHelper from './SchemaHelper';
1415
import FetchResultsHelper from './FetchResultsHelper';
1516
import CompleteOperationHelper from './CompleteOperationHelper';
17+
import IDBSQLLogger, { LogLevel } from '../contracts/IDBSQLLogger';
1618

1719
export default class DBSQLOperation implements IOperation {
1820
private driver: HiveDriver;
1921

2022
private operationHandle: TOperationHandle;
2123

24+
private logger: IDBSQLLogger;
25+
2226
private _status: OperationStatusHelper;
2327

2428
private _schema: SchemaHelper;
@@ -27,9 +31,15 @@ export default class DBSQLOperation implements IOperation {
2731

2832
private _completeOperation: CompleteOperationHelper;
2933

30-
constructor(driver: HiveDriver, operationHandle: TOperationHandle, directResults?: TSparkDirectResults) {
34+
constructor(
35+
driver: HiveDriver,
36+
operationHandle: TOperationHandle,
37+
logger: IDBSQLLogger,
38+
directResults?: TSparkDirectResults,
39+
) {
3140
this.driver = driver;
3241
this.operationHandle = operationHandle;
42+
this.logger = logger;
3343
this._status = new OperationStatusHelper(this.driver, this.operationHandle, directResults?.operationStatus);
3444
this._schema = new SchemaHelper(this.driver, this.operationHandle, directResults?.resultSetMetadata);
3545
this._data = new FetchResultsHelper(this.driver, this.operationHandle, [directResults?.resultSet]);
@@ -38,6 +48,11 @@ export default class DBSQLOperation implements IOperation {
3848
this.operationHandle,
3949
directResults?.closeOperation,
4050
);
51+
this.logger.log(LogLevel.debug, `Operation created with id: ${this.getId()}`);
52+
}
53+
54+
getId() {
55+
return stringify(this.operationHandle?.operationId?.guid || parse(NIL));
4156
}
4257

4358
/**
@@ -56,6 +71,7 @@ export default class DBSQLOperation implements IOperation {
5671
const chunk = await this.fetchChunk(options);
5772
data.push(chunk);
5873
} while (await this.hasMoreRows()); // eslint-disable-line no-await-in-loop
74+
this.logger?.log(LogLevel.debug, `Fetched all data from operation with id: ${this.getId()}`);
5975

6076
return data.flat();
6177
}
@@ -79,6 +95,10 @@ export default class DBSQLOperation implements IOperation {
7995
return Promise.all([this._schema.fetch(), this._data.fetch(options?.maxRows || defaultMaxRows)]).then(
8096
([schema, data]) => {
8197
const result = getResult(schema, data ? [data] : []);
98+
this.logger?.log(
99+
LogLevel.debug,
100+
`Fetched chunk of size: ${options?.maxRows || defaultMaxRows} from operation with id: ${this.getId()}`,
101+
);
82102
return Promise.resolve(result);
83103
},
84104
);
@@ -90,6 +110,7 @@ export default class DBSQLOperation implements IOperation {
90110
* @throws {StatusError}
91111
*/
92112
async status(progress: boolean = false): Promise<TGetOperationStatusResp> {
113+
this.logger?.log(LogLevel.debug, `Fetching status for operation with id: ${this.getId()}`);
93114
return this._status.status(progress);
94115
}
95116

@@ -98,6 +119,7 @@ export default class DBSQLOperation implements IOperation {
98119
* @throws {StatusError}
99120
*/
100121
cancel(): Promise<Status> {
122+
this.logger?.log(LogLevel.debug, `Operation with id: ${this.getId()} canceled.`);
101123
return this._completeOperation.cancel();
102124
}
103125

@@ -106,6 +128,7 @@ export default class DBSQLOperation implements IOperation {
106128
* @throws {StatusError}
107129
*/
108130
close(): Promise<Status> {
131+
this.logger?.log(LogLevel.debug, `Closing operation with id: ${this.getId()}`);
109132
return this._completeOperation.close();
110133
}
111134

@@ -126,6 +149,7 @@ export default class DBSQLOperation implements IOperation {
126149
}
127150

128151
await this._status.waitUntilReady(options);
152+
this.logger?.log(LogLevel.debug, `Fetching schema for operation with id: ${this.getId()}`);
129153

130154
return this._schema.fetch();
131155
}

lib/DBSQLSession.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { stringify, NIL, parse } from 'uuid';
12
import { TSessionHandle, TStatus, TOperationHandle, TSparkDirectResults } from '../thrift/TCLIService_types';
23
import HiveDriver from './hive/HiveDriver';
34
import { Int64 } from './hive/Types';
@@ -19,6 +20,7 @@ import Status from './dto/Status';
1920
import StatusFactory from './factory/StatusFactory';
2021
import InfoValue from './dto/InfoValue';
2122
import { definedOrError } from './utils';
23+
import IDBSQLLogger, { LogLevel } from './contracts/IDBSQLLogger';
2224

2325
interface OperationResponseShape {
2426
status: TStatus;
@@ -45,10 +47,18 @@ export default class DBSQLSession implements IDBSQLSession {
4547

4648
private statusFactory: StatusFactory;
4749

48-
constructor(driver: HiveDriver, sessionHandle: TSessionHandle) {
50+
private logger: IDBSQLLogger;
51+
52+
constructor(driver: HiveDriver, sessionHandle: TSessionHandle, logger: IDBSQLLogger) {
4953
this.driver = driver;
5054
this.sessionHandle = sessionHandle;
5155
this.statusFactory = new StatusFactory();
56+
this.logger = logger;
57+
this.logger.log(LogLevel.debug, `Session created with id: ${this.getId()}`);
58+
}
59+
60+
getId() {
61+
return stringify(this.sessionHandle?.sessionId?.guid || parse(NIL));
5262
}
5363

5464
/**
@@ -322,13 +332,16 @@ export default class DBSQLSession implements IDBSQLSession {
322332
.closeSession({
323333
sessionHandle: this.sessionHandle,
324334
})
325-
.then((response) => this.statusFactory.create(response.status));
335+
.then((response) => {
336+
this.logger.log(LogLevel.debug, `Session closed with id: ${this.getId()}`);
337+
return this.statusFactory.create(response.status);
338+
});
326339
}
327340

328341
private createOperation(response: OperationResponseShape): IOperation {
329342
this.assertStatus(response.status);
330343
const handle = definedOrError(response.operationHandle);
331-
return new DBSQLOperation(this.driver, handle, response.directResults);
344+
return new DBSQLOperation(this.driver, handle, this.logger, response.directResults);
332345
}
333346

334347
private assertStatus(responseStatus: TStatus): void {

lib/contracts/IDBSQLClient.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1+
import IDBSQLLogger from './IDBSQLLogger';
12
import IDBSQLSession from './IDBSQLSession';
23

4+
export interface ClientOptions {
5+
logger?: IDBSQLLogger;
6+
}
7+
38
export interface ConnectionOptions {
49
host: string;
510
port?: number;

lib/contracts/IDBSQLLogger.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export default interface IDBSQLLogger {
2+
log(level: LogLevel, message: string): void;
3+
}
4+
5+
export enum LogLevel {
6+
error = 'error',
7+
warn = 'warn',
8+
info = 'info',
9+
debug = 'debug',
10+
}

lib/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ import TCLIService from '../thrift/TCLIService';
22
import TCLIService_types from '../thrift/TCLIService_types';
33
import DBSQLClient from './DBSQLClient';
44
import DBSQLSession from './DBSQLSession';
5+
import DBSQLLogger from './DBSQLLogger';
56
import NoSaslAuthentication from './connection/auth/NoSaslAuthentication';
67
import PlainHttpAuthentication from './connection/auth/PlainHttpAuthentication';
78
import HttpConnection from './connection/connections/HttpConnection';
89
import { formatProgress } from './utils';
10+
import { LogLevel } from './contracts/IDBSQLLogger';
911

1012
export const auth = {
1113
NoSaslAuthentication,
@@ -25,4 +27,4 @@ export const utils = {
2527
formatProgress,
2628
};
2729

28-
export { DBSQLClient, DBSQLSession };
30+
export { DBSQLClient, DBSQLSession, DBSQLLogger, LogLevel };

0 commit comments

Comments
 (0)