blob: 47900f86e035d29725b6df5fc11446c7c7992566 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
const assert = require('assert');
const helper = require('../test-helper');
const types = require('../../lib/types');
const RequestLogger = require('../../lib/tracker').RequestLogger;
const ExecutionOptions = require('../../lib/execution-options').ExecutionOptions;
describe('RequestLogger', () => {
describe('#onSuccess()', () => {
context('when request is slow', () => {
const logger = new RequestLogger({ slowThreshold: 200 });
const loggerShortMessage = new RequestLogger({
slowThreshold: 200, messageMaxQueryLength: 20, messageMaxParameterValueLength: 5 });
let message;
const handler = (m) => message = m;
logger.emitter.on('slow', handler);
loggerShortMessage.emitter.on('slow', handler);
beforeEach(() => message = null);
it('should include the query and parameters in the log message', () => {
const query = 'INSERT EXAMPLE';
logger.onSuccess({ address: '::1' }, query, ['a', true, 1.01], getExecutionOptions(false), 10, 20, [ 1, 2034125 ]);
helper.assertContains(message, query, true);
helper.assertContains(message, 'Slow request, took 1002 ms');
helper.assertContains(message, '[a,true,1.01]');
helper.assertContains(message, 'not prepared');
});
it('should stringify object parameters in the log message', () => {
const query = 'UPDATE EXAMPLE';
logger.onSuccess({ address: '::1' }, query, { a: 1, b: true, c: 1.01 }, getExecutionOptions(), 10, 20, [ 1, 0 ]);
helper.assertContains(message, query, true);
helper.assertContains(message, 'Slow request, took 1000 ms');
helper.assertContains(message, '[a:1,b:true,c:1.01]');
helper.assertContains(message, 'not prepared');
});
it('should stringify individual object values', () => {
const query = 'INSERT SAMPLE (k1, k2) VALUES (?, ?)';
const params = [ types.Uuid.random(), types.InetAddress.fromString('10.10.10.1') ];
logger.onSuccess({ address: '::1' }, query, params, getExecutionOptions(true), 1, 2, [ 2, 0 ]);
helper.assertContains(message, `INSERT SAMPLE (k1, k2) VALUES (?, ?) [${params.join(',')}]`);
});
it('should include size information and round to KB', () => {
logger.onSuccess({ address: '::1' }, 'Q', [], getExecutionOptions(), 12700, 231, [ 1, 0 ]);
helper.assertContains(message, 'request size 12 KB / response size 231 bytes');
});
it('should include batch information and queries', () => {
const queries = [ { query: 'Q1' }, { query: 'Q2', params: [ 'a', 'b' ]}];
logger.onSuccess({ address: '::1' }, queries, null, getExecutionOptions(), 80, 231, [ 1, 0 ]);
helper.assertContains(message, 'request size 80 bytes / response size 231 bytes');
helper.assertContains(message, 'BATCH w/ 2 not prepared queries (Q1 [],Q2 [a,b])');
});
it('should include batch information and truncate queries info', () => {
const queries = [ { query: 'INSERT1' }, { query: 'INSERT2' }, { query: 'INSERT3', params: [ 'a', 'b' ]}];
loggerShortMessage.onSuccess({ address: '::1' }, queries, null, getExecutionOptions(), 1, 1, [ 1, 0 ]);
helper.assertContains(message, 'BATCH w/ 3 not prepared queries (INSERT1 [],INSERT2 [...],...)');
});
it('should truncate query in the message when query is too long', () => {
const query = 'INSERT EXECUTE A B C this_should_not_be_included';
loggerShortMessage.onSuccess({ address: '::1' }, query, ['a', true], getExecutionOptions(true), 1, 2, [ 2, 0 ]);
helper.assertContains(message, 'Slow request, took 2000 ms');
helper.assertContains(message, 'INSERT EXECUTE A B C [...]');
assert.strictEqual(message.indexOf('this_should_not_be_included'), -1);
});
it('should truncate parameters in the message when parameters are too long', () => {
const query = 'INSERT';
const params = ['abcdefghijk', 1];
loggerShortMessage.onSuccess({ address: '::1' }, query, params, getExecutionOptions(true), 1, 2, [ 2, 0 ]);
helper.assertContains(message, 'Slow request, took 2000 ms');
helper.assertContains(message, 'INSERT [abcde,1]');
});
it('should truncate parameters in the message when query and parameters are too long', () => {
const query = 'INSERT SAMPLE';
const params = ['abcde', true, 1];
loggerShortMessage.onSuccess({ address: '::1' }, query, params, getExecutionOptions(true), 1, 2, [ 2, 0 ]);
helper.assertContains(message, 'Slow request, took 2000 ms');
helper.assertContains(message, 'INSERT SAMPLE [abcde,...]');
});
});
context('when request is large', () => {
const logger = new RequestLogger({ requestSizeThreshold: 500 });
let message;
logger.emitter.on('large', m => message = m);
beforeEach(() => message = null);
it('should include the query and parameters in the log message', () => {
const query = 'INSERT EXAMPLE';
logger.onSuccess({ address: '::1' }, query, ['a', true, 1.01], getExecutionOptions(false), 1000, 20, [ 0, 201 ]);
helper.assertContains(message, query, true);
helper.assertContains(message, 'Request exceeded length');
helper.assertContains(message, 'INSERT EXAMPLE [a,true,1.01]');
helper.assertContains(message, 'not prepared');
});
it('should not include query preparation info when has been prepared', () => {
const query = 'UPDATE EXAMPLE';
logger.onSuccess({ address: '::1' }, query, [], getExecutionOptions(true), 1000, 20, [ 0, 201 ]);
helper.assertContains(message, query, true);
helper.assertContains(message, 'Request exceeded length');
assert.strictEqual(message.indexOf('prepared'), -1);
});
});
context('when request is normal', () => {
const logger = new RequestLogger({ slowThreshold: 200, requestSizeThreshold: 500 });
let message;
const otherMessages = [];
const handler = (m) => otherMessages.push(m);
logger.emitter.on('normal', m => message = m);
logger.emitter.on('slow', handler)
.on('large', handler)
.on('failure', handler);
beforeEach(() => {
message = null;
otherMessages.length = 0;
});
it('should not log normal requests by default', () => {
logger.onSuccess({ address: '::1' }, 'QUERY', [], getExecutionOptions(), 1, 1, [ 0, 1 ]);
// Disabled initially
assert.strictEqual(message, null);
assert.strictEqual(otherMessages.length, 0);
});
it('should log normal requests when logNormalRequests is enabled', () => {
logger.logNormalRequests = true;
const query = 'SELECT EXAMPLE';
logger.onSuccess({ address: '::1' }, query, [], getExecutionOptions(), 1, 1, [ 0, 1 ]);
assert.deepEqual(otherMessages, []);
helper.assertContains(message, query, true);
helper.assertContains(message, 'Request completed normally');
helper.assertContains(message, 'SELECT EXAMPLE []');
helper.assertContains(message, 'not prepared');
});
});
});
describe('#onError()', () => {
const logger = new RequestLogger({ requestSizeThreshold: 500, logErroredRequests: true });
let largeMessage;
let errorMessage;
logger.emitter.on('large', m => largeMessage = m);
logger.emitter.on('failure', m => errorMessage = m);
beforeEach(() => {
largeMessage = null;
errorMessage = null;
logger.logErroredRequests = true;
});
context('when request is large', () => {
it('should include stack trace when large request failed', () => {
const err = new Error('Sample error');
logger.onError({ address: '::1' }, 'Q', [1, 2, 'a'], getExecutionOptions(), 27891, err, [ 1, 0 ]);
assert.strictEqual(errorMessage, null);
helper.assertContains(largeMessage, 'Request exceeded length and execution failed');
helper.assertContains(largeMessage, 'request size 27 KB');
helper.assertContains(largeMessage, '[1,2,a]');
helper.assertContains(largeMessage, err.stack.substr(0, 200));
});
});
context('when logErroredRequests is enabled initially', () => {
it('should include stack trace when large request failed', () => {
const err = new Error('Sample error');
logger.onError({ address: '::1' }, 'Q', [1, 2, 'a'], getExecutionOptions(), 80, err, [ 1, 0 ]);
assert.strictEqual(largeMessage, null);
helper.assertContains(errorMessage, 'Request execution failed');
helper.assertContains(errorMessage, err.stack.substr(0, 200));
});
it('should allow to be disabled using logErroredRequests property', () => {
const err = new Error('Sample error');
logger.onError({ address: '::1' }, 'Q', [1, 2, 'a'], getExecutionOptions(), 80, err, [ 1, 0 ]);
assert.strictEqual(typeof errorMessage, 'string');
// Disable it
logger.logErroredRequests = false;
errorMessage = null;
logger.onError({ address: '::1' }, 'Q', [1, 2, 'a'], getExecutionOptions(), 80, err, [ 1, 0 ]);
assert.strictEqual(errorMessage, null);
});
});
});
});
function getExecutionOptions(prepare) {
const result = ExecutionOptions.empty();
result.isPrepared = () => prepare;
return result;
}