├── .babelrc ├── .eslintrc ├── .flowconfig ├── .gitignore ├── .prettierrc ├── LICENSE.md ├── README.md ├── package.json ├── src ├── __typings__ │ └── test.js └── index.js ├── typings ├── index.d.ts ├── test.ts ├── tsconfig.json └── tslint.json └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["@babel/preset-env", { 4 | "targets": { 5 | "node": "10" 6 | }, 7 | "modules": "commonjs", 8 | "bugfixes": true, 9 | "loose": true 10 | }], 11 | "@babel/preset-flow" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "plugin:prettier/recommended", 4 | "plugin:import/errors", 5 | "standard", 6 | "prettier", 7 | "prettier/standard" 8 | ], 9 | "parser": "babel-eslint", 10 | "plugins": [ 11 | "flowtype" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | /lib/.* 3 | 4 | [include] 5 | 6 | [libs] 7 | 8 | [lints] 9 | 10 | [options] 11 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectError 12 | 13 | [strict] 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | lib/ 3 | 4 | .vscode 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "semi": false, 4 | "printWidth": 120 5 | } 6 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Maxim Khvatalin (http://github.com/khmm12) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # knex-tiny-logger 2 | 3 | [![](https://img.shields.io/npm/v/knex-tiny-logger.svg?style=flat-square)](https://npmjs.com/package/knex-tiny-logger) 4 | [![](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](https://standardjs.com) 5 | 6 | > Zero config queries logger for knex 7 | 8 | ![](https://cloud.githubusercontent.com/assets/4437249/24814454/5215bd9c-1bda-11e7-8574-5f93042395dd.png) 9 | 10 | ## Usage 11 | 12 | Install the package: 13 | 14 | ```bash 15 | $ yarn add knex-tiny-logger 16 | ``` 17 | 18 | Apply `knex-tiny-logger` to `knex` instance: 19 | 20 | ```js 21 | import createKnex from 'knex' 22 | import knexTinyLogger from 'knex-tiny-logger' 23 | 24 | const knexOptions = {} // Your knex config 25 | const knex = createKnex(knexOptions) 26 | knexTinyLogger(knex) 27 | 28 | // alternative 29 | // knex-tiny-logger returns knex instance 30 | // so you can do like this 31 | const knex = knexTinyLogger(createKnex(knexOptions)) 32 | ``` 33 | 34 | ## Advanced usage 35 | 36 | By default `knex-tiny-logger` uses `console.log`, but you can specify any logger which your prefer: 37 | ```js 38 | import createKnex from 'knex' 39 | import knexTinyLogger from 'knex-tiny-logger' 40 | import initDebug from 'debug' 41 | 42 | const awesomeLogger = initDebug('my-project:knex') 43 | const knexOptions = {} // Your knex config 44 | const knex = createKnex(knexOptions) 45 | knexTinyLogger(knex, { logger: awesomeLogger }) 46 | ``` 47 | 48 | Also you can disable bindings: 49 | ```js 50 | knexTinyLogger(knex, { bindings: false }) 51 | ``` 52 | 53 | ## License 54 | 55 | [MIT](LICENSE.md) 56 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "knex-tiny-logger", 3 | "version": "2.1.0", 4 | "description": "Tiny logger for knex", 5 | "repository": { 6 | "type": "git", 7 | "url": "git://github.com/khmm12/knex-tiny-logger.git" 8 | }, 9 | "author": "Maixm Khvatalin (http://github.com/khmm12)", 10 | "files": [ 11 | "/lib", 12 | "/typings/index.d.ts" 13 | ], 14 | "keywords": [ 15 | "knex", 16 | "logger" 17 | ], 18 | "license": "MIT", 19 | "main": "lib/index.js", 20 | "engines": { 21 | "node": ">=10" 22 | }, 23 | "typings": "typings", 24 | "sideEffects": false, 25 | "scripts": { 26 | "test": "run-p --aggregate-output test:*", 27 | "test:lint": "eslint src", 28 | "test:flow": "flow check", 29 | "test:ts": "dtslint ./typings", 30 | "build": "run-s build:clean build:cjs build:flow", 31 | "build:clean": "rm -rf lib", 32 | "build:cjs": "babel src --ignore src/__typings__ --out-dir lib", 33 | "build:flow": "flow-copy-source --ignore '__typings__/*.js' src lib", 34 | "precommit": "test" 35 | }, 36 | "devDependencies": { 37 | "@babel/cli": "^7.8.4", 38 | "@babel/core": "^7.9.0", 39 | "@babel/preset-env": "^7.9.5", 40 | "@babel/preset-flow": "^7.9.0", 41 | "@types/node": "^13.11.1", 42 | "babel-eslint": "10", 43 | "dtslint": "^3.4.1", 44 | "eslint": "^6.8.0", 45 | "eslint-config-prettier": "^6.10.1", 46 | "eslint-config-standard": "14.1.1", 47 | "eslint-plugin-flowtype": "^4.7.0", 48 | "eslint-plugin-import": "^2.20.2", 49 | "eslint-plugin-node": "^11.1.0", 50 | "eslint-plugin-prettier": "^3.1.2", 51 | "eslint-plugin-promise": "^4.2.1", 52 | "eslint-plugin-standard": "^4.0.1", 53 | "flow-bin": "^0.122.0", 54 | "flow-copy-source": "^2.0.9", 55 | "knex": "^0.20.13", 56 | "npm-run-all": "^4.1.5", 57 | "prettier": "^2.0.4", 58 | "typescript": "^3.8.3" 59 | }, 60 | "peerDependencies": { 61 | "knex": "*" 62 | }, 63 | "dependencies": { 64 | "chalk": "^4.1.0" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/__typings__/test.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import createKnex from 'knex' 4 | import knexTinyLogger from '..' 5 | 6 | const knexOptions = {} 7 | const knex = createKnex(knexOptions) 8 | 9 | knexTinyLogger(knex) 10 | 11 | knexTinyLogger(knex, { 12 | logger: console.log, 13 | }) 14 | 15 | knexTinyLogger(knex, { 16 | bindings: true, 17 | }) 18 | 19 | // $FlowExpectError: should be absolute or fixed 20 | knexTinyLogger(null) 21 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import type Knex from 'knex' 4 | import chalk from 'chalk' 5 | 6 | export type KnexTinyLoggerOptions = { 7 | logger?: (message?: any, ...optionalParams: any[]) => void, 8 | bindings?: boolean, 9 | } 10 | 11 | type KnexTinyLogger$StartTime = [number, number] 12 | 13 | type KnexTinyLogger$Query = { 14 | sql: string, 15 | bindings: any, 16 | startTime: KnexTinyLogger$StartTime, 17 | } 18 | 19 | type KnexTinyLogger$KnexFormatQuery = (sql: string, bindings: any) => string 20 | type Knex$QueryExecutionerFormat = (sql: string, bindings: any, timeZone?: string, client: Knex) => string 21 | 22 | const COLORIZE = { 23 | primary: chalk.magenta, 24 | error: chalk.red, 25 | success: chalk.cyan, 26 | } 27 | 28 | /** 29 | * Decorate `knex` instance with logger 30 | * 31 | * @param {Object} knex - knex instance 32 | * @param {Object} options 33 | * @param {Function} [options.logger=console.log] 34 | * @param {Boolean} [options.bindings=true] 35 | * @return {Object} knex - knex instance 36 | */ 37 | 38 | export default function knexTinyLogger(knex: Knex, options?: KnexTinyLoggerOptions = {}): Knex { 39 | const { logger = console.log, bindings: withBindings = true } = options 40 | const queries: Map = new Map() 41 | const print = makeQueryPrinter(knex, { logger, withBindings }) 42 | 43 | return knex.on('query', handleQuery).on('query-error', handleQueryError).on('query-response', handleQueryResponse) 44 | 45 | function handleQuery({ __knexQueryUid: queryId, sql, bindings }) { 46 | const startTime = measureStartTime() 47 | queries.set(queryId, { sql, bindings, startTime }) 48 | } 49 | 50 | function handleQueryError(_error, { __knexQueryUid: queryId }) { 51 | withQuery(queryId, ({ sql, bindings, duration }) => { 52 | print({ sql, bindings, duration }, COLORIZE.error) 53 | }) 54 | } 55 | 56 | function handleQueryResponse(_response, { __knexQueryUid: queryId }) { 57 | withQuery(queryId, ({ sql, bindings, duration }) => { 58 | print({ sql, bindings, duration }, COLORIZE.success) 59 | }) 60 | } 61 | 62 | function withQuery(queryId, fn) { 63 | const query = queries.get(queryId) 64 | queries.delete(queryId) 65 | if (!query) throw new TypeError('Query disappeared') 66 | const { sql, bindings, startTime } = query 67 | const duration = measureDuration(startTime) 68 | fn({ sql, bindings, duration }) 69 | } 70 | } 71 | 72 | function makeQueryPrinter(knex: Knex, { logger, withBindings }) { 73 | const formatQuery = getKnexFormatQuery(knex) 74 | 75 | return function print({ sql, bindings, duration }, colorize: Function) { 76 | const sqlRequest = formatQuery(sql, withBindings ? bindings : null) 77 | 78 | logger('%s %s', COLORIZE.primary(`SQL (${duration.toFixed(3)} ms)`), colorize(sqlRequest)) 79 | } 80 | } 81 | 82 | function measureStartTime() { 83 | return process.hrtime() 84 | } 85 | 86 | function measureDuration(startTime: KnexTinyLogger$StartTime): number { 87 | const diff = process.hrtime(startTime) 88 | const duration = diff[0] * 1e3 + diff[1] * 1e-6 89 | return duration 90 | } 91 | 92 | function getKnexFormatQuery(knex: Knex): KnexTinyLogger$KnexFormatQuery { 93 | let queryExecutionerFormat: ?Knex$QueryExecutionerFormat 94 | 95 | if (typeof knex.client._formatQuery === 'function') { 96 | return (sql, bindings) => knex.client._formatQuery(sql, bindings) 97 | } else if ((queryExecutionerFormat = resolveQueryExecutionerFormat()) != null) { 98 | // $FlowExpectError 99 | return (sql, bindings) => queryExecutionerFormat(sql, bindings, undefined, knex) 100 | } else { 101 | return (sql) => sql 102 | } 103 | } 104 | 105 | function resolveQueryExecutionerFormat(): ?Knex$QueryExecutionerFormat { 106 | try { 107 | // $FlowExpectError 108 | const { formatQuery } = require('knex/lib/execution/internal/query-executioner') 109 | return typeof formatQuery === 'function' ? formatQuery : null 110 | } catch { 111 | return null 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /typings/index.d.ts: -------------------------------------------------------------------------------- 1 | // Minimum TypeScript Version: 3.4 2 | 3 | import Knex = require('knex') 4 | 5 | export interface KnexTinyLoggerOptions { 6 | logger?: (message?: any, ...optionalParams: any[]) => void, 7 | bindings?: boolean 8 | } 9 | 10 | export default function knexTinyLogger(knex: Knex, options?: KnexTinyLoggerOptions): Knex 11 | -------------------------------------------------------------------------------- /typings/test.ts: -------------------------------------------------------------------------------- 1 | import createKnex from 'knex' 2 | import knexTinyLogger from 'knex-tiny-logger' 3 | 4 | const knexOptions = {} 5 | const knex = createKnex(knexOptions) 6 | 7 | knexTinyLogger(knex) 8 | 9 | knexTinyLogger(knex, { 10 | logger: console.log 11 | }) 12 | 13 | knexTinyLogger(knex, { 14 | bindings: true 15 | }) 16 | 17 | knexTinyLogger(null) // $ExpectError 18 | -------------------------------------------------------------------------------- /typings/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "es2015", 5 | "lib": ["es6"], 6 | "noEmit": true, 7 | "strict": true, 8 | "moduleResolution": "node", 9 | "allowSyntheticDefaultImports": true, 10 | "baseUrl": ".", 11 | "paths": { 12 | "knex-tiny-logger": ["."] 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /typings/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "dtslint/dtslint.json", 3 | "rules": { 4 | "semicolon": false 5 | } 6 | } 7 | --------------------------------------------------------------------------------