slonik-interceptor-query-logging 1.3.8 → 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/LICENSE +2 -2
  2. package/README.md +7 -14
  3. package/dist/src/factories/createQueryLoggingInterceptor.d.ts +9 -0
  4. package/dist/src/factories/createQueryLoggingInterceptor.js +78 -0
  5. package/dist/src/factories/index.d.ts +1 -0
  6. package/dist/src/factories/index.js +5 -0
  7. package/dist/src/index.d.ts +1 -0
  8. package/dist/src/index.js +6 -0
  9. package/dist/src/utilities/getAutoExplainPayload.d.ts +1 -0
  10. package/dist/src/utilities/getAutoExplainPayload.js +15 -0
  11. package/dist/src/utilities/index.d.ts +2 -0
  12. package/dist/src/utilities/index.js +7 -0
  13. package/dist/src/utilities/isAutoExplainJsonMessage.d.ts +1 -0
  14. package/dist/src/utilities/isAutoExplainJsonMessage.js +7 -0
  15. package/package.json +29 -51
  16. package/src/factories/{createQueryLoggingInterceptor.js → createQueryLoggingInterceptor.ts} +21 -26
  17. package/src/{index.js → factories/index.ts} +1 -3
  18. package/{dist/index.js.flow → src/index.ts} +0 -0
  19. package/src/utilities/{getAutoExplainPayload.js → getAutoExplainPayload.ts} +1 -3
  20. package/src/utilities/index.ts +6 -0
  21. package/{dist/utilities/isAutoExplainJsonMessage.js.flow → src/utilities/isAutoExplainJsonMessage.ts} +1 -3
  22. package/tsconfig.json +26 -0
  23. package/.flowconfig +0 -3
  24. package/dist/factories/createQueryLoggingInterceptor.js +0 -113
  25. package/dist/factories/createQueryLoggingInterceptor.js.flow +0 -106
  26. package/dist/factories/createQueryLoggingInterceptor.js.map +0 -1
  27. package/dist/factories/index.js +0 -16
  28. package/dist/factories/index.js.flow +0 -3
  29. package/dist/factories/index.js.map +0 -1
  30. package/dist/index.js +0 -14
  31. package/dist/index.js.map +0 -1
  32. package/dist/utilities/getAutoExplainPayload.js +0 -26
  33. package/dist/utilities/getAutoExplainPayload.js.flow +0 -19
  34. package/dist/utilities/getAutoExplainPayload.js.map +0 -1
  35. package/dist/utilities/index.js +0 -24
  36. package/dist/utilities/index.js.flow +0 -4
  37. package/dist/utilities/index.js.map +0 -1
  38. package/dist/utilities/isAutoExplainJsonMessage.js +0 -14
  39. package/dist/utilities/isAutoExplainJsonMessage.js.map +0 -1
  40. package/src/factories/index.js +0 -3
  41. package/src/utilities/index.js +0 -4
  42. package/src/utilities/isAutoExplainJsonMessage.js +0 -5
package/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2019, Gajus Kuizinas (http://gajus.com/)
1
+ Copyright (c) 2021, Gajus Kuizinas (http://gajus.com/)
2
2
  All rights reserved.
3
3
 
4
4
  Redistribution and use in source and binary forms, with or without
@@ -15,7 +15,7 @@ modification, are permitted provided that the following conditions are met:
15
15
  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16
16
  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17
17
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
- DISCLAIMED. IN NO EVENT SHALL ANUARY BE LIABLE FOR ANY
18
+ DISCLAIMED. IN NO EVENT SHALL GAJUS KUIZINAS BE LIABLE FOR ANY
19
19
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20
20
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
21
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # slonik-interceptor-query-logging
2
2
 
3
- [![Travis build status](http://img.shields.io/travis/gajus/slonik-interceptor-query-logging/master.svg?style=flat-square)](https://travis-ci.org/gajus/slonik-interceptor-query-logging)
3
+ [![Travis build status](http://img.shields.io/travis/gajus/slonik-interceptor-query-logging/master.svg?style=flat-square)](https://travis-ci.com/github/gajus/slonik-interceptor-query-logging)
4
4
  [![Coveralls](https://img.shields.io/coveralls/gajus/slonik-interceptor-query-logging.svg?style=flat-square)](https://coveralls.io/github/gajus/slonik-interceptor-query-logging)
5
5
  [![NPM version](http://img.shields.io/npm/v/slonik-interceptor-query-logging.svg?style=flat-square)](https://www.npmjs.org/package/slonik-interceptor-query-logging)
6
6
  [![Canonical Code Style](https://img.shields.io/badge/code%20style-canonical-blue.svg?style=flat-square)](https://github.com/gajus/canonical)
@@ -10,14 +10,13 @@ Logs [Slonik](https://github.com/gajus/slonik) queries.
10
10
 
11
11
  ## API
12
12
 
13
- ```js
13
+ ```ts
14
14
  import {
15
15
  createQueryLoggingInterceptor
16
- } from 'slonik';
17
-
16
+ } from 'slonik-interceptor-query-logging';
18
17
  ```
19
18
 
20
- ```js
19
+ ```ts
21
20
  /**
22
21
  * @property logValues Dictates whether to include parameter values used to execute the query. (default: true)
23
22
  */
@@ -26,14 +25,13 @@ type UserConfigurationType = {|
26
25
  |};
27
26
 
28
27
  (userConfiguration: UserConfigurationType) => InterceptorType;
29
-
30
28
  ```
31
29
 
32
30
  ## Example usage
33
31
 
34
32
  Note: You must export `ROARR_LOG=true` environment variable for logs to be printed. Refer to [Roarr](https://github.com/gajus/roarr) documentation for more information.
35
33
 
36
- ```js
34
+ ```ts
37
35
  import {
38
36
  createPool
39
37
  } from 'slonik';
@@ -55,18 +53,16 @@ await connection.any(sql`
55
53
  code_alpha_2
56
54
  FROM country
57
55
  `);
58
-
59
56
  ```
60
57
 
61
58
  Produces log:
62
59
 
63
- ```sql
60
+ ```json
64
61
  {"context":{"package":"slonik","poolId":"01D4G1EHYPYX1DZ073G08QEPED","logLevel":30,"processId":769420982,"stats":{"idleConnectionCount":0,"totalConnectionCount":1,"waitingRequestCount":0}},"message":"created a new client connection","sequence":0,"time":1551021590799,"version":"1.0.0"}
65
62
  {"context":{"package":"slonik","poolId":"01D4G1EHYPYX1DZ073G08QEPED","logLevel":30,"processId":769420982,"stats":{"idleConnectionCount":0,"totalConnectionCount":1,"waitingRequestCount":0}},"message":"client is checked out from the pool","sequence":1,"time":1551021590801,"version":"1.0.0"}
66
63
  {"context":{"package":"slonik","poolId":"01D4G1EHYPYX1DZ073G08QEPED","connectionId":"01D4G1EJ8DZFX79EN8CRE5Z7VS","queryId":"01D4G1EJB9P3SJ3V0SSVRTZ800","logLevel":20,"sql":"SELECT id, code_alpha_2 FROM country","stackTrace":[],"values":[]},"message":"executing query","sequence":2,"time":1551021590890,"version":"1.0.0"}
67
64
  {"context":{"package":"slonik","poolId":"01D4G1EHYPYX1DZ073G08QEPED","connectionId":"01D4G1EJ8DZFX79EN8CRE5Z7VS","queryId":"01D4G1EJB9P3SJ3V0SSVRTZ800","logLevel":20,"executionTime":"65ms","rowCount":250},"message":"query execution result","sequence":3,"time":1551021590918,"version":"1.0.0"}
68
65
  {"context":{"package":"slonik","poolId":"01D4G1EHYPYX1DZ073G08QEPED","logLevel":30,"processId":769420982,"stats":{"idleConnectionCount":0,"totalConnectionCount":0,"waitingRequestCount":0}},"message":"client connection is closed and removed from the client pool","sequence":4,"time":1551021600922,"version":"1.0.0"}
69
-
70
66
  ```
71
67
 
72
68
  Use [`roarr-cli`](https://github.com/gajus/roarr-cli) to pretty print the logs.
@@ -114,7 +110,6 @@ stats:
114
110
  idleConnectionCount: 0
115
111
  totalConnectionCount: 0
116
112
  waitingRequestCount: 0
117
-
118
113
  ```
119
114
 
120
115
  ## Logging `auto_explain`
@@ -137,12 +132,11 @@ SET auto_explain.log_timing=true;
137
132
 
138
133
  -- Enables Slonik to capture auto_explain logs.
139
134
  SET client_min_messages=log;
140
-
141
135
  ```
142
136
 
143
137
  This can be configured using `afterPoolConnection` interceptor, e.g.
144
138
 
145
- ```js
139
+ ```ts
146
140
  const pool = createPool('postgres://localhost', {
147
141
  interceptors: [
148
142
  {
@@ -196,5 +190,4 @@ notice:
196
190
  Actual Total Time: 15.29
197
191
  Actual Rows: 195786
198
192
  Actual Loops: 1
199
-
200
193
  ```
@@ -0,0 +1,9 @@
1
+ import type { InterceptorType } from 'slonik';
2
+ /**
3
+ * @property logValues Dictates whether to include parameter values used to execute the query. (default: true)
4
+ */
5
+ declare type UserConfigurationType = {
6
+ logValues: boolean;
7
+ };
8
+ export declare const createQueryLoggingInterceptor: (userConfiguration?: UserConfigurationType | undefined) => InterceptorType;
9
+ export {};
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createQueryLoggingInterceptor = void 0;
7
+ const pretty_ms_1 = __importDefault(require("pretty-ms"));
8
+ const serialize_error_1 = require("serialize-error");
9
+ const utilities_1 = require("../utilities");
10
+ const stringifyCallSite = (callSite) => {
11
+ return (callSite.fileName || '') + ':' + callSite.lineNumber + ':' + callSite.columnNumber;
12
+ };
13
+ const defaultConfiguration = {
14
+ logValues: true,
15
+ };
16
+ const createQueryLoggingInterceptor = (userConfiguration) => {
17
+ const configuration = {
18
+ ...defaultConfiguration,
19
+ ...userConfiguration,
20
+ };
21
+ return {
22
+ afterQueryExecution: (context, query, result) => {
23
+ let rowCount = null;
24
+ if (result.rowCount) {
25
+ rowCount = result.rowCount;
26
+ }
27
+ for (const notice of result.notices) {
28
+ if ((0, utilities_1.isAutoExplainJsonMessage)(notice.message)) {
29
+ context.log.info({
30
+ autoExplain: (0, utilities_1.getAutoExplainPayload)(notice.message),
31
+ }, 'auto explain');
32
+ }
33
+ }
34
+ context.log.debug({
35
+ executionTime: (0, pretty_ms_1.default)(Number(process.hrtime.bigint() - BigInt(context.queryInputTime)) / 1000000),
36
+ rowCount,
37
+ }, 'query execution result');
38
+ return null;
39
+ },
40
+ beforeQueryExecution: async (context, query) => {
41
+ let stackTrace;
42
+ if (context.stackTrace) {
43
+ stackTrace = [];
44
+ for (const callSite of context.stackTrace) {
45
+ // Hide the internal call sites.
46
+ if (callSite.fileName !== null && !callSite.fileName.includes('slonik') && !callSite.fileName.includes('next_tick')) {
47
+ stackTrace.push(stringifyCallSite(callSite));
48
+ }
49
+ }
50
+ }
51
+ let values;
52
+ if (configuration.logValues) {
53
+ values = [];
54
+ for (const value of query.values) {
55
+ if (Buffer.isBuffer(value)) {
56
+ values.push('[Buffer ' + value.byteLength + ']');
57
+ }
58
+ else {
59
+ values.push(value);
60
+ }
61
+ }
62
+ }
63
+ context.log.debug({
64
+ sql: query.sql,
65
+ stackTrace,
66
+ values,
67
+ }, 'executing query');
68
+ return null;
69
+ },
70
+ queryExecutionError: (context, query, error) => {
71
+ context.log.error({
72
+ error: (0, serialize_error_1.serializeError)(error),
73
+ }, 'query execution produced an error');
74
+ return null;
75
+ },
76
+ };
77
+ };
78
+ exports.createQueryLoggingInterceptor = createQueryLoggingInterceptor;
@@ -0,0 +1 @@
1
+ export { createQueryLoggingInterceptor, } from './createQueryLoggingInterceptor';
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createQueryLoggingInterceptor = void 0;
4
+ var createQueryLoggingInterceptor_1 = require("./createQueryLoggingInterceptor");
5
+ Object.defineProperty(exports, "createQueryLoggingInterceptor", { enumerable: true, get: function () { return createQueryLoggingInterceptor_1.createQueryLoggingInterceptor; } });
@@ -0,0 +1 @@
1
+ export { createQueryLoggingInterceptor, } from './factories';
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ // @flow
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.createQueryLoggingInterceptor = void 0;
5
+ var factories_1 = require("./factories");
6
+ Object.defineProperty(exports, "createQueryLoggingInterceptor", { enumerable: true, get: function () { return factories_1.createQueryLoggingInterceptor; } });
@@ -0,0 +1 @@
1
+ export declare const getAutoExplainPayload: (noticeMessage: string) => any;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getAutoExplainPayload = void 0;
4
+ const crack_json_1 = require("crack-json");
5
+ const getAutoExplainPayload = (noticeMessage) => {
6
+ const matches = (0, crack_json_1.extractJson)(noticeMessage);
7
+ if (matches.length === 0) {
8
+ throw new Error('Notice message does not contain a recognizable JSON payload.');
9
+ }
10
+ if (matches.length > 1) {
11
+ throw new Error('Notice message contains multiple JSON payloads.');
12
+ }
13
+ return matches[0];
14
+ };
15
+ exports.getAutoExplainPayload = getAutoExplainPayload;
@@ -0,0 +1,2 @@
1
+ export { getAutoExplainPayload, } from './getAutoExplainPayload';
2
+ export { isAutoExplainJsonMessage, } from './isAutoExplainJsonMessage';
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isAutoExplainJsonMessage = exports.getAutoExplainPayload = void 0;
4
+ var getAutoExplainPayload_1 = require("./getAutoExplainPayload");
5
+ Object.defineProperty(exports, "getAutoExplainPayload", { enumerable: true, get: function () { return getAutoExplainPayload_1.getAutoExplainPayload; } });
6
+ var isAutoExplainJsonMessage_1 = require("./isAutoExplainJsonMessage");
7
+ Object.defineProperty(exports, "isAutoExplainJsonMessage", { enumerable: true, get: function () { return isAutoExplainJsonMessage_1.isAutoExplainJsonMessage; } });
@@ -0,0 +1 @@
1
+ export declare const isAutoExplainJsonMessage: (noticeMessage: string) => boolean;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isAutoExplainJsonMessage = void 0;
4
+ const isAutoExplainJsonMessage = (noticeMessage) => {
5
+ return noticeMessage.trim().startsWith('duration:') && noticeMessage.includes('{');
6
+ };
7
+ exports.isAutoExplainJsonMessage = isAutoExplainJsonMessage;
package/package.json CHANGED
@@ -5,46 +5,40 @@
5
5
  "url": "http://gajus.com"
6
6
  },
7
7
  "ava": {
8
- "babel": {
9
- "compileAsTests": [
10
- "test/helpers/**/*"
11
- ]
12
- },
8
+ "extensions": [
9
+ "ts"
10
+ ],
13
11
  "files": [
14
12
  "test/slonik-interceptor-query-logging/**/*"
15
13
  ],
16
14
  "require": [
17
- "@babel/register"
15
+ "ts-node/register/transpile-only"
18
16
  ]
19
17
  },
20
18
  "dependencies": {
21
19
  "crack-json": "^1.3.0",
22
- "pretty-ms": "^6.0.0",
23
- "serialize-error": "^5.0.0",
24
- "slonik": "^22.4.0"
20
+ "pretty-ms": "^7.0.1",
21
+ "serialize-error": "^9.0.0"
25
22
  },
26
23
  "description": "Logs Slonik queries.",
27
24
  "devDependencies": {
28
- "@ava/babel": "^1.0.1",
29
- "@babel/cli": "^7.8.4",
30
- "@babel/core": "^7.8.4",
31
- "@babel/plugin-transform-flow-strip-types": "^7.8.3",
32
- "@babel/preset-env": "^7.8.4",
33
- "@babel/register": "^7.8.3",
34
- "ava": "^3.3.0",
35
- "babel-plugin-istanbul": "^6.0.0",
36
- "babel-plugin-macros": "^2.8.0",
37
- "babel-plugin-transform-export-default-name": "^2.0.4",
38
- "coveralls": "^3.0.9",
39
- "eslint": "^6.8.0",
40
- "eslint-config-canonical": "^18.1.1",
41
- "flow-bin": "^0.118.0",
42
- "flow-copy-source": "^2.0.9",
43
- "husky": "^4.2.3",
25
+ "@istanbuljs/nyc-config-typescript": "^1.0.1",
26
+ "ava": "^3.15.0",
27
+ "babel-plugin-istanbul": "^6.1.1",
28
+ "babel-plugin-macros": "^3.1.0",
29
+ "babel-plugin-transform-export-default-name": "^2.1.0",
30
+ "coveralls": "^3.1.1",
31
+ "del-cli": "^4.0.1",
32
+ "eslint": "^8.3.0",
33
+ "eslint-config-canonical": "^32.43.0",
34
+ "husky": "^7.0.4",
44
35
  "inline-loops.macro": "^1.2.2",
45
- "nyc": "^15.0.0",
46
- "semantic-release": "^17.0.3",
47
- "sinon": "^8.1.1"
36
+ "nyc": "^15.1.0",
37
+ "semantic-release": "^18.0.1",
38
+ "sinon": "^12.0.1",
39
+ "slonik": "^22.4.0",
40
+ "ts-node": "^10.4.0",
41
+ "typescript": "^4.5.2"
48
42
  },
49
43
  "engines": {
50
44
  "node": ">=8.0"
@@ -60,36 +54,20 @@
60
54
  "logging"
61
55
  ],
62
56
  "license": "BSD-3-Clause",
63
- "main": "./dist/index.js",
57
+ "main": "./dist/src/index.js",
64
58
  "name": "slonik-interceptor-query-logging",
65
- "nyc": {
66
- "all": true,
67
- "exclude": [
68
- "src/bin",
69
- "src/queries/*.js"
70
- ],
71
- "include": [
72
- "src/**/*.js"
73
- ],
74
- "instrument": false,
75
- "reporter": [
76
- "html",
77
- "text-summary"
78
- ],
79
- "require": [
80
- "@babel/register"
81
- ],
82
- "silent": true,
83
- "sourceMap": false
59
+ "peerDependencies": {
60
+ "slonik": ">=25.1.2"
84
61
  },
85
62
  "repository": {
86
63
  "type": "git",
87
64
  "url": "https://github.com/gajus/slonik-interceptor-query-logging"
88
65
  },
89
66
  "scripts": {
90
- "build": "rm -fr ./dist && NODE_ENV=production babel ./src --out-dir ./dist --copy-files --source-maps && flow-copy-source src dist",
91
- "lint": "eslint ./src ./test && flow",
67
+ "build": "del-cli ./dist && tsc",
68
+ "lint": "eslint ./src ./test && tsc --noEmit",
92
69
  "test": "NODE_ENV=test nyc ava --verbose --serial"
93
70
  },
94
- "version": "1.3.8"
71
+ "typings": "./dist/src/index.d.ts",
72
+ "version": "1.4.2"
95
73
  }
@@ -1,16 +1,10 @@
1
- // @flow
2
-
1
+ import prettyMs from 'pretty-ms';
3
2
  import {
4
3
  serializeError,
5
4
  } from 'serialize-error';
6
5
  import type {
7
6
  InterceptorType,
8
7
  } from 'slonik';
9
- import {
10
- filter,
11
- map,
12
- } from 'inline-loops.macro';
13
- import prettyMs from 'pretty-ms';
14
8
  import {
15
9
  getAutoExplainPayload,
16
10
  isAutoExplainJsonMessage,
@@ -19,9 +13,9 @@ import {
19
13
  /**
20
14
  * @property logValues Dictates whether to include parameter values used to execute the query. (default: true)
21
15
  */
22
- type UserConfigurationType = {|
23
- +logValues: boolean,
24
- |};
16
+ type UserConfigurationType = {
17
+ logValues: boolean,
18
+ };
25
19
 
26
20
  const stringifyCallSite = (callSite) => {
27
21
  return (callSite.fileName || '') + ':' + callSite.lineNumber + ':' + callSite.columnNumber;
@@ -31,7 +25,7 @@ const defaultConfiguration = {
31
25
  logValues: true,
32
26
  };
33
27
 
34
- export default (userConfiguration?: UserConfigurationType): InterceptorType => {
28
+ export const createQueryLoggingInterceptor = (userConfiguration?: UserConfigurationType): InterceptorType => {
35
29
  const configuration = {
36
30
  ...defaultConfiguration,
37
31
  ...userConfiguration,
@@ -54,7 +48,7 @@ export default (userConfiguration?: UserConfigurationType): InterceptorType => {
54
48
  }
55
49
 
56
50
  context.log.debug({
57
- executionTime: prettyMs(Number(process.hrtime.bigint() - context.queryInputTime) / 1000000),
51
+ executionTime: prettyMs(Number(process.hrtime.bigint() - BigInt(context.queryInputTime)) / 1_000_000),
58
52
  rowCount,
59
53
  }, 'query execution result');
60
54
 
@@ -64,27 +58,28 @@ export default (userConfiguration?: UserConfigurationType): InterceptorType => {
64
58
  let stackTrace;
65
59
 
66
60
  if (context.stackTrace) {
67
- stackTrace = map(
68
- filter(context.stackTrace, (callSite) => {
69
- // Hide the internal call sites.
70
- return callSite.fileName !== null && !callSite.fileName.includes('slonik') && !callSite.fileName.includes('next_tick');
71
- }),
72
- (callSite) => {
73
- return stringifyCallSite(callSite);
74
- },
75
- );
61
+ stackTrace = [];
62
+
63
+ for (const callSite of context.stackTrace) {
64
+ // Hide the internal call sites.
65
+ if (callSite.fileName !== null && !callSite.fileName.includes('slonik') && !callSite.fileName.includes('next_tick')) {
66
+ stackTrace.push(stringifyCallSite(callSite));
67
+ }
68
+ }
76
69
  }
77
70
 
78
71
  let values;
79
72
 
80
73
  if (configuration.logValues) {
81
- values = map(query.values, (value) => {
74
+ values = [];
75
+
76
+ for (const value of query.values) {
82
77
  if (Buffer.isBuffer(value)) {
83
- return '[Buffer ' + value.byteLength + ']';
78
+ values.push('[Buffer ' + value.byteLength + ']');
79
+ } else {
80
+ values.push(value);
84
81
  }
85
-
86
- return value;
87
- });
82
+ }
88
83
  }
89
84
 
90
85
  context.log.debug({
@@ -1,5 +1,3 @@
1
- // @flow
2
-
3
1
  export {
4
2
  createQueryLoggingInterceptor,
5
- } from './factories';
3
+ } from './createQueryLoggingInterceptor';
File without changes
@@ -1,10 +1,8 @@
1
- // @flow
2
-
3
1
  import {
4
2
  extractJson,
5
3
  } from 'crack-json';
6
4
 
7
- export default (noticeMessage: string) => {
5
+ export const getAutoExplainPayload = (noticeMessage: string) => {
8
6
  const matches = extractJson(noticeMessage);
9
7
 
10
8
  if (matches.length === 0) {
@@ -0,0 +1,6 @@
1
+ export {
2
+ getAutoExplainPayload,
3
+ } from './getAutoExplainPayload';
4
+ export {
5
+ isAutoExplainJsonMessage,
6
+ } from './isAutoExplainJsonMessage';
@@ -1,5 +1,3 @@
1
- // @flow
2
-
3
- export default (noticeMessage: string): boolean => {
1
+ export const isAutoExplainJsonMessage = (noticeMessage: string): boolean => {
4
2
  return noticeMessage.trim().startsWith('duration:') && noticeMessage.includes('{');
5
3
  };
package/tsconfig.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "compilerOptions": {
3
+ "allowSyntheticDefaultImports": true,
4
+ "declaration": true,
5
+ "esModuleInterop": true,
6
+ "forceConsistentCasingInFileNames": true,
7
+ "module": "commonjs",
8
+ "moduleResolution": "node",
9
+ "noImplicitAny": false,
10
+ "noImplicitReturns": true,
11
+ "noUnusedLocals": true,
12
+ "noUnusedParameters": false,
13
+ "outDir": "dist",
14
+ "skipLibCheck": true,
15
+ "strict": true,
16
+ "target": "es2018"
17
+ },
18
+ "exclude": [
19
+ "dist",
20
+ "node_modules"
21
+ ],
22
+ "include": [
23
+ "src",
24
+ "test"
25
+ ]
26
+ }
package/.flowconfig DELETED
@@ -1,3 +0,0 @@
1
- [ignore]
2
- .*/node_modules/.*/test/.*
3
- <PROJECT_ROOT>/dist/.*
@@ -1,113 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = void 0;
7
-
8
- var _serializeError = require("serialize-error");
9
-
10
- var _prettyMs = _interopRequireDefault(require("pretty-ms"));
11
-
12
- var _utilities = require("../utilities");
13
-
14
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15
-
16
- const stringifyCallSite = callSite => {
17
- return (callSite.fileName || '') + ':' + callSite.lineNumber + ':' + callSite.columnNumber;
18
- };
19
-
20
- const defaultConfiguration = {
21
- logValues: true
22
- };
23
-
24
- const createQueryLoggingInterceptor = userConfiguration => {
25
- const configuration = { ...defaultConfiguration,
26
- ...userConfiguration
27
- };
28
- return {
29
- afterQueryExecution: (context, query, result) => {
30
- let rowCount = null;
31
-
32
- if (result.rowCount) {
33
- rowCount = result.rowCount;
34
- }
35
-
36
- for (const notice of result.notices) {
37
- if ((0, _utilities.isAutoExplainJsonMessage)(notice.message)) {
38
- context.log.info({
39
- autoExplain: (0, _utilities.getAutoExplainPayload)(notice.message)
40
- }, 'auto explain');
41
- }
42
- }
43
-
44
- context.log.debug({
45
- executionTime: (0, _prettyMs.default)(Number(process.hrtime.bigint() - context.queryInputTime) / 1000000),
46
- rowCount
47
- }, 'query execution result');
48
- return null;
49
- },
50
- beforeQueryExecution: async (context, query) => {
51
- let stackTrace;
52
-
53
- if (context.stackTrace) {
54
- const _iterable = context.stackTrace;
55
- let _result = [];
56
-
57
- for (let _key = 0, _length = _iterable.length, _value; _key < _length; ++_key) {
58
- _value = _iterable[_key];
59
- if (_value.fileName !== null && !_value.fileName.includes('slonik') && !_value.fileName.includes('next_tick')) _result.push(_value);
60
- }
61
-
62
- let _result2 = [];
63
-
64
- for (let _key2 = 0, _length2 = _result.length, _value2; _key2 < _length2; ++_key2) {
65
- _value2 = _result[_key2];
66
- _result2[_key2] = stringifyCallSite(_value2);
67
- }
68
-
69
- stackTrace = _result2;
70
- }
71
-
72
- let values;
73
-
74
- if (configuration.logValues) {
75
- const _iterable3 = query.values;
76
-
77
- const _fn3 = value => {
78
- if (Buffer.isBuffer(value)) {
79
- return '[Buffer ' + value.byteLength + ']';
80
- }
81
-
82
- return value;
83
- };
84
-
85
- let _result3 = [];
86
-
87
- for (let _key3 = 0, _length3 = _iterable3.length, _value3; _key3 < _length3; ++_key3) {
88
- _value3 = _iterable3[_key3];
89
- _result3[_key3] = _fn3(_value3, _key3, _iterable3);
90
- }
91
-
92
- values = _result3;
93
- }
94
-
95
- context.log.debug({
96
- sql: query.sql,
97
- stackTrace,
98
- values
99
- }, 'executing query');
100
- return null;
101
- },
102
- queryExecutionError: (context, query, error) => {
103
- context.log.error({
104
- error: (0, _serializeError.serializeError)(error)
105
- }, 'query execution produced an error');
106
- return null;
107
- }
108
- };
109
- };
110
-
111
- var _default = createQueryLoggingInterceptor;
112
- exports.default = _default;
113
- //# sourceMappingURL=createQueryLoggingInterceptor.js.map
@@ -1,106 +0,0 @@
1
- // @flow
2
-
3
- import {
4
- serializeError,
5
- } from 'serialize-error';
6
- import type {
7
- InterceptorType,
8
- } from 'slonik';
9
- import {
10
- filter,
11
- map,
12
- } from 'inline-loops.macro';
13
- import prettyMs from 'pretty-ms';
14
- import {
15
- getAutoExplainPayload,
16
- isAutoExplainJsonMessage,
17
- } from '../utilities';
18
-
19
- /**
20
- * @property logValues Dictates whether to include parameter values used to execute the query. (default: true)
21
- */
22
- type UserConfigurationType = {|
23
- +logValues: boolean,
24
- |};
25
-
26
- const stringifyCallSite = (callSite) => {
27
- return (callSite.fileName || '') + ':' + callSite.lineNumber + ':' + callSite.columnNumber;
28
- };
29
-
30
- const defaultConfiguration = {
31
- logValues: true,
32
- };
33
-
34
- export default (userConfiguration?: UserConfigurationType): InterceptorType => {
35
- const configuration = {
36
- ...defaultConfiguration,
37
- ...userConfiguration,
38
- };
39
-
40
- return {
41
- afterQueryExecution: (context, query, result) => {
42
- let rowCount: number | null = null;
43
-
44
- if (result.rowCount) {
45
- rowCount = result.rowCount;
46
- }
47
-
48
- for (const notice of result.notices) {
49
- if (isAutoExplainJsonMessage(notice.message)) {
50
- context.log.info({
51
- autoExplain: getAutoExplainPayload(notice.message),
52
- }, 'auto explain');
53
- }
54
- }
55
-
56
- context.log.debug({
57
- executionTime: prettyMs(Number(process.hrtime.bigint() - context.queryInputTime) / 1000000),
58
- rowCount,
59
- }, 'query execution result');
60
-
61
- return null;
62
- },
63
- beforeQueryExecution: async (context, query) => {
64
- let stackTrace;
65
-
66
- if (context.stackTrace) {
67
- stackTrace = map(
68
- filter(context.stackTrace, (callSite) => {
69
- // Hide the internal call sites.
70
- return callSite.fileName !== null && !callSite.fileName.includes('slonik') && !callSite.fileName.includes('next_tick');
71
- }),
72
- (callSite) => {
73
- return stringifyCallSite(callSite);
74
- },
75
- );
76
- }
77
-
78
- let values;
79
-
80
- if (configuration.logValues) {
81
- values = map(query.values, (value) => {
82
- if (Buffer.isBuffer(value)) {
83
- return '[Buffer ' + value.byteLength + ']';
84
- }
85
-
86
- return value;
87
- });
88
- }
89
-
90
- context.log.debug({
91
- sql: query.sql,
92
- stackTrace,
93
- values,
94
- }, 'executing query');
95
-
96
- return null;
97
- },
98
- queryExecutionError: (context, query, error) => {
99
- context.log.error({
100
- error: serializeError(error),
101
- }, 'query execution produced an error');
102
-
103
- return null;
104
- },
105
- };
106
- };
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/factories/createQueryLoggingInterceptor.js"],"names":["stringifyCallSite","callSite","fileName","lineNumber","columnNumber","defaultConfiguration","logValues","userConfiguration","configuration","afterQueryExecution","context","query","result","rowCount","notice","notices","message","log","info","autoExplain","debug","executionTime","Number","process","hrtime","bigint","queryInputTime","beforeQueryExecution","stackTrace","includes","values","value","Buffer","isBuffer","byteLength","sql","queryExecutionError","error"],"mappings":"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","sourcesContent":["// @flow\n\nimport {\n serializeError,\n} from 'serialize-error';\nimport type {\n InterceptorType,\n} from 'slonik';\nimport {\n filter,\n map,\n} from 'inline-loops.macro';\nimport prettyMs from 'pretty-ms';\nimport {\n getAutoExplainPayload,\n isAutoExplainJsonMessage,\n} from '../utilities';\n\n/**\n * @property logValues Dictates whether to include parameter values used to execute the query. (default: true)\n */\ntype UserConfigurationType = {|\n +logValues: boolean,\n|};\n\nconst stringifyCallSite = (callSite) => {\n return (callSite.fileName || '') + ':' + callSite.lineNumber + ':' + callSite.columnNumber;\n};\n\nconst defaultConfiguration = {\n logValues: true,\n};\n\nexport default (userConfiguration?: UserConfigurationType): InterceptorType => {\n const configuration = {\n ...defaultConfiguration,\n ...userConfiguration,\n };\n\n return {\n afterQueryExecution: (context, query, result) => {\n let rowCount: number | null = null;\n\n if (result.rowCount) {\n rowCount = result.rowCount;\n }\n\n for (const notice of result.notices) {\n if (isAutoExplainJsonMessage(notice.message)) {\n context.log.info({\n autoExplain: getAutoExplainPayload(notice.message),\n }, 'auto explain');\n }\n }\n\n context.log.debug({\n executionTime: prettyMs(Number(process.hrtime.bigint() - context.queryInputTime) / 1000000),\n rowCount,\n }, 'query execution result');\n\n return null;\n },\n beforeQueryExecution: async (context, query) => {\n let stackTrace;\n\n if (context.stackTrace) {\n stackTrace = map(\n filter(context.stackTrace, (callSite) => {\n // Hide the internal call sites.\n return callSite.fileName !== null && !callSite.fileName.includes('slonik') && !callSite.fileName.includes('next_tick');\n }),\n (callSite) => {\n return stringifyCallSite(callSite);\n },\n );\n }\n\n let values;\n\n if (configuration.logValues) {\n values = map(query.values, (value) => {\n if (Buffer.isBuffer(value)) {\n return '[Buffer ' + value.byteLength + ']';\n }\n\n return value;\n });\n }\n\n context.log.debug({\n sql: query.sql,\n stackTrace,\n values,\n }, 'executing query');\n\n return null;\n },\n queryExecutionError: (context, query, error) => {\n context.log.error({\n error: serializeError(error),\n }, 'query execution produced an error');\n\n return null;\n },\n };\n};\n"],"file":"createQueryLoggingInterceptor.js"}
@@ -1,16 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- Object.defineProperty(exports, "createQueryLoggingInterceptor", {
7
- enumerable: true,
8
- get: function () {
9
- return _createQueryLoggingInterceptor.default;
10
- }
11
- });
12
-
13
- var _createQueryLoggingInterceptor = _interopRequireDefault(require("./createQueryLoggingInterceptor"));
14
-
15
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
16
- //# sourceMappingURL=index.js.map
@@ -1,3 +0,0 @@
1
- // @flow
2
-
3
- export {default as createQueryLoggingInterceptor} from './createQueryLoggingInterceptor';
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/factories/index.js"],"names":[],"mappings":";;;;;;;;;;;;AAEA","sourcesContent":["// @flow\n\nexport {default as createQueryLoggingInterceptor} from './createQueryLoggingInterceptor';\n"],"file":"index.js"}
package/dist/index.js DELETED
@@ -1,14 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- Object.defineProperty(exports, "createQueryLoggingInterceptor", {
7
- enumerable: true,
8
- get: function () {
9
- return _factories.createQueryLoggingInterceptor;
10
- }
11
- });
12
-
13
- var _factories = require("./factories");
14
- //# sourceMappingURL=index.js.map
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/index.js"],"names":[],"mappings":";;;;;;;;;;;;AAEA","sourcesContent":["// @flow\n\nexport {\n createQueryLoggingInterceptor,\n} from './factories';\n"],"file":"index.js"}
@@ -1,26 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = void 0;
7
-
8
- var _crackJson = require("crack-json");
9
-
10
- const getAutoExplainPayload = noticeMessage => {
11
- const matches = (0, _crackJson.extractJson)(noticeMessage);
12
-
13
- if (matches.length === 0) {
14
- throw new Error('Notice message does not contain a recognizable JSON payload.');
15
- }
16
-
17
- if (matches.length > 1) {
18
- throw new Error('Notice message contains multiple JSON payloads.');
19
- }
20
-
21
- return matches[0];
22
- };
23
-
24
- var _default = getAutoExplainPayload;
25
- exports.default = _default;
26
- //# sourceMappingURL=getAutoExplainPayload.js.map
@@ -1,19 +0,0 @@
1
- // @flow
2
-
3
- import {
4
- extractJson,
5
- } from 'crack-json';
6
-
7
- export default (noticeMessage: string) => {
8
- const matches = extractJson(noticeMessage);
9
-
10
- if (matches.length === 0) {
11
- throw new Error('Notice message does not contain a recognizable JSON payload.');
12
- }
13
-
14
- if (matches.length > 1) {
15
- throw new Error('Notice message contains multiple JSON payloads.');
16
- }
17
-
18
- return matches[0];
19
- };
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/utilities/getAutoExplainPayload.js"],"names":["noticeMessage","matches","length","Error"],"mappings":";;;;;;;AAEA;;8BAIgBA,a,IAA0B;AACxC,QAAMC,OAAO,GAAG,4BAAYD,aAAZ,CAAhB;;AAEA,MAAIC,OAAO,CAACC,MAAR,KAAmB,CAAvB,EAA0B;AACxB,UAAM,IAAIC,KAAJ,CAAU,8DAAV,CAAN;AACD;;AAED,MAAIF,OAAO,CAACC,MAAR,GAAiB,CAArB,EAAwB;AACtB,UAAM,IAAIC,KAAJ,CAAU,iDAAV,CAAN;AACD;;AAED,SAAOF,OAAO,CAAC,CAAD,CAAd;AACD,C","sourcesContent":["// @flow\n\nimport {\n extractJson,\n} from 'crack-json';\n\nexport default (noticeMessage: string) => {\n const matches = extractJson(noticeMessage);\n\n if (matches.length === 0) {\n throw new Error('Notice message does not contain a recognizable JSON payload.');\n }\n\n if (matches.length > 1) {\n throw new Error('Notice message contains multiple JSON payloads.');\n }\n\n return matches[0];\n};\n"],"file":"getAutoExplainPayload.js"}
@@ -1,24 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- Object.defineProperty(exports, "getAutoExplainPayload", {
7
- enumerable: true,
8
- get: function () {
9
- return _getAutoExplainPayload.default;
10
- }
11
- });
12
- Object.defineProperty(exports, "isAutoExplainJsonMessage", {
13
- enumerable: true,
14
- get: function () {
15
- return _isAutoExplainJsonMessage.default;
16
- }
17
- });
18
-
19
- var _getAutoExplainPayload = _interopRequireDefault(require("./getAutoExplainPayload"));
20
-
21
- var _isAutoExplainJsonMessage = _interopRequireDefault(require("./isAutoExplainJsonMessage"));
22
-
23
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
24
- //# sourceMappingURL=index.js.map
@@ -1,4 +0,0 @@
1
- // @flow
2
-
3
- export {default as getAutoExplainPayload} from './getAutoExplainPayload';
4
- export {default as isAutoExplainJsonMessage} from './isAutoExplainJsonMessage';
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/utilities/index.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAEA;;AACA","sourcesContent":["// @flow\n\nexport {default as getAutoExplainPayload} from './getAutoExplainPayload';\nexport {default as isAutoExplainJsonMessage} from './isAutoExplainJsonMessage';\n"],"file":"index.js"}
@@ -1,14 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = void 0;
7
-
8
- const isAutoExplainJsonMessage = noticeMessage => {
9
- return noticeMessage.trim().startsWith('duration:') && noticeMessage.includes('{');
10
- };
11
-
12
- var _default = isAutoExplainJsonMessage;
13
- exports.default = _default;
14
- //# sourceMappingURL=isAutoExplainJsonMessage.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/utilities/isAutoExplainJsonMessage.js"],"names":["noticeMessage","trim","startsWith","includes"],"mappings":";;;;;;;iCAEgBA,a,IAAmC;AACjD,SAAOA,aAAa,CAACC,IAAd,GAAqBC,UAArB,CAAgC,WAAhC,KAAgDF,aAAa,CAACG,QAAd,CAAuB,GAAvB,CAAvD;AACD,C","sourcesContent":["// @flow\n\nexport default (noticeMessage: string): boolean => {\n return noticeMessage.trim().startsWith('duration:') && noticeMessage.includes('{');\n};\n"],"file":"isAutoExplainJsonMessage.js"}
@@ -1,3 +0,0 @@
1
- // @flow
2
-
3
- export {default as createQueryLoggingInterceptor} from './createQueryLoggingInterceptor';
@@ -1,4 +0,0 @@
1
- // @flow
2
-
3
- export {default as getAutoExplainPayload} from './getAutoExplainPayload';
4
- export {default as isAutoExplainJsonMessage} from './isAutoExplainJsonMessage';
@@ -1,5 +0,0 @@
1
- // @flow
2
-
3
- export default (noticeMessage: string): boolean => {
4
- return noticeMessage.trim().startsWith('duration:') && noticeMessage.includes('{');
5
- };