Skip to content

Commit f2fc68c

Browse files
committed
Support configurable logging
This commit makes driver able to log internal events to the configured logging destination. Produced log messages should give an insight into what operations are happening inside the driver. Logging destination can be configured for the driver in the config object: ``` const config = { logger: (level, message) => console.log(level + ' ' + message) }; const driver = neo4j.driver( 'bolt://localhost', neo4j.auth.basic('neo4j', 'password'), config ); ``` Property `logger` should be a function that takes two arguments `level: string` and `message: string`. Log level can now be 'error', 'warn', 'info' or 'debug'. Function should not execute any blocking or long-running operations because it is often invoked on a hot path. There exists a predefined logger implementation which outputs timestamp, log level and message. Example: ``` 1527703465096 INFO Direct driver 0 created for server address localhost:7687 ``` It is defined in the `neo4j.logger` object and can be used like this: ``` const config = { logger: neo4j.logger.console }; const driver = neo4j.driver( 'bolt://localhost', neo4j.auth.basic('neo4j', 'password'), config ); ``` Users of the driver should be able to plug in an existing logging framework like https://github.com/winstonjs/winston.
1 parent 2c2ae55 commit f2fc68c

14 files changed

+400
-59
lines changed

src/v1/driver.js

+27-13
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {DirectConnectionProvider} from './internal/connection-providers';
2626
import Bookmark from './internal/bookmark';
2727
import ConnectivityVerifier from './internal/connectivity-verifier';
2828
import PoolConfig, {DEFAULT_ACQUISITION_TIMEOUT, DEFAULT_MAX_SIZE} from './internal/pool-config';
29+
import Logger from './internal/logger';
2930

3031
const DEFAULT_MAX_CONNECTION_LIFETIME = 60 * 60 * 1000; // 1 hour
3132

@@ -43,6 +44,8 @@ const READ = 'READ';
4344
*/
4445
const WRITE = 'WRITE';
4546

47+
let idGenerator = 0;
48+
4649
/**
4750
* A driver maintains one or more {@link Session}s with a remote
4851
* Neo4j instance. Through the {@link Session}s you can send statements
@@ -66,17 +69,19 @@ class Driver {
6669
constructor(hostPort, userAgent, token = {}, config = {}) {
6770
sanitizeConfig(config);
6871

72+
this._id = idGenerator++;
6973
this._hostPort = hostPort;
7074
this._userAgent = userAgent;
71-
this._openSessions = {};
72-
this._sessionIdGenerator = 0;
75+
this._openConnections = {};
7376
this._token = token;
7477
this._config = config;
78+
this._log = Logger.create(config);
7579
this._pool = new Pool(
7680
this._createConnection.bind(this),
7781
this._destroyConnection.bind(this),
7882
this._validateConnection.bind(this),
79-
PoolConfig.fromDriverConfig(config)
83+
PoolConfig.fromDriverConfig(config),
84+
this._log
8085
);
8186

8287
/**
@@ -87,6 +92,15 @@ class Driver {
8792
this._connectionProvider = null;
8893

8994
this._onCompleted = null;
95+
96+
this._afterConstruction();
97+
}
98+
99+
/**
100+
* @protected
101+
*/
102+
_afterConstruction() {
103+
this._log.info(`Direct driver ${this._id} created for server address ${this._hostPort}`);
90104
}
91105

92106
/**
@@ -118,14 +132,12 @@ class Driver {
118132
* @access private
119133
*/
120134
_createConnection(hostPort, release) {
121-
let sessionId = this._sessionIdGenerator++;
122-
let conn = connect(hostPort, this._config, this._connectionErrorCode());
135+
let conn = connect(hostPort, this._config, this._connectionErrorCode(), this._log);
123136
let streamObserver = new _ConnectionStreamObserver(this, conn);
124137
conn.initialize(this._userAgent, this._token, streamObserver);
125-
conn._id = sessionId;
126138
conn._release = () => release(hostPort, conn);
127139

128-
this._openSessions[sessionId] = conn;
140+
this._openConnections[conn.id] = conn;
129141
return conn;
130142
}
131143

@@ -145,12 +157,12 @@ class Driver {
145157
}
146158

147159
/**
148-
* Dispose of a live session, closing any associated resources.
149-
* @return {Session} new session.
160+
* Dispose of a connection.
161+
* @return {Connection} the connection to dispose.
150162
* @access private
151163
*/
152164
_destroyConnection(conn) {
153-
delete this._openSessions[conn._id];
165+
delete this._openConnections[conn.id];
154166
conn.close();
155167
}
156168

@@ -224,9 +236,11 @@ class Driver {
224236
* @return undefined
225237
*/
226238
close() {
227-
for (let sessionId in this._openSessions) {
228-
if (this._openSessions.hasOwnProperty(sessionId)) {
229-
this._openSessions[sessionId].close();
239+
this._log.info(`Driver ${this._id} closing`);
240+
241+
for (let connectionId in this._openConnections) {
242+
if (this._openConnections.hasOwnProperty(connectionId)) {
243+
this._openConnections[connectionId].close();
230244
}
231245
this._pool.purgeAll();
232246
}

src/v1/index.js

+17
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,14 @@ const auth = {
6868
};
6969
const USER_AGENT = "neo4j-javascript/" + VERSION;
7070

71+
/**
72+
* Object containing predefined logger implementations. These are expected to be used as values of the driver config's <code>logger</code> property.
73+
* @property {function(level: string, message: string)} console the logger function that outputs timestamp, log level and message to <code>console.log()</code>
74+
*/
75+
const logger = {
76+
console: (level, message) => console.log(`${global.Date.now()} ${level.toUpperCase()} ${message}`)
77+
};
78+
7179
/**
7280
* Construct a new Neo4j Driver. This is your main entry point for this
7381
* library.
@@ -172,6 +180,13 @@ const USER_AGENT = "neo4j-javascript/" + VERSION;
172180
* // Default value for this option is <code>false</code> because native JavaScript numbers might result
173181
* // in loss of precision in the general case.
174182
* disableLosslessIntegers: false,
183+
*
184+
* // Specify the logger function for this driver. Function should take two arguments:
185+
* // 1) <code>level</code> - string representing the log level. Supported levels are: 'error', 'warn', 'info' and 'debug'
186+
* // 2) <code>message</code> - string representing the log message.
187+
* // Function should not execute any blocking or long-running operations because it is often executed on a hot path.
188+
* // No logging is done by default. See <code>neo4j.logger</code> object that contains predefined logger implementations.
189+
* logger: (level, message) => console.log(level + ' ' + message),
175190
* }
176191
*
177192
* @param {string} url The URL for the Neo4j database, for instance "bolt://localhost"
@@ -280,6 +295,7 @@ const forExport = {
280295
integer,
281296
Neo4jError,
282297
auth,
298+
logger,
283299
types,
284300
session,
285301
error,
@@ -301,6 +317,7 @@ export {
301317
integer,
302318
Neo4jError,
303319
auth,
320+
logger,
304321
types,
305322
session,
306323
error,

src/v1/internal/ch-node.js

-1
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,6 @@ class NodeChannel {
372372
if( this._pending !== null ) {
373373
this._pending.push( buffer );
374374
} else if( buffer instanceof NodeBuffer ) {
375-
// console.log( "[Conn#"+this.id+"] SEND: ", buffer.toString() );
376375
this._conn.write( buffer._buffer );
377376
} else {
378377
throw newError( "Don't know how to write: " + buffer );

src/v1/internal/connection-providers.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export class DirectConnectionProvider extends ConnectionProvider {
6060

6161
export class LoadBalancer extends ConnectionProvider {
6262

63-
constructor(hostPort, routingContext, connectionPool, loadBalancingStrategy, driverOnErrorCallback) {
63+
constructor(hostPort, routingContext, connectionPool, loadBalancingStrategy, driverOnErrorCallback, log) {
6464
super();
6565
this._seedRouter = hostPort;
6666
this._routingTable = new RoutingTable([this._seedRouter]);
@@ -69,6 +69,7 @@ export class LoadBalancer extends ConnectionProvider {
6969
this._driverOnErrorCallback = driverOnErrorCallback;
7070
this._hostNameResolver = LoadBalancer._createHostNameResolver();
7171
this._loadBalancingStrategy = loadBalancingStrategy;
72+
this._log = log;
7273
this._useSeedRouter = false;
7374
}
7475

@@ -111,6 +112,7 @@ export class LoadBalancer extends ConnectionProvider {
111112
if (!currentRoutingTable.isStaleFor(accessMode)) {
112113
return Promise.resolve(currentRoutingTable);
113114
}
115+
this._log.info(`Routing table is stale for ${accessMode}: ${currentRoutingTable}`);
114116
return this._refreshRoutingTable(currentRoutingTable);
115117
}
116118

@@ -235,6 +237,7 @@ export class LoadBalancer extends ConnectionProvider {
235237

236238
// make this driver instance aware of the new table
237239
this._routingTable = newRoutingTable;
240+
this._log.info(`Updated routing table ${newRoutingTable}`);
238241
}
239242

240243
static _forgetRouter(routingTable, routersArray, routerIndex) {

0 commit comments

Comments
 (0)