Repository: salsita/node-pg-migrate (ts)

Issue:
Allow to disable query logging
Would you consider allowing to disable logging for the SQL queries? 

For migration scripts generating a lot of queries we found that the synchronous nature of `console.log()` (in Unix like environments at least) was making our scripts very slow (an order from 1 to 5, old but still relevant article as to why https://www.daveeddy.com/2012/12/06/high-performance-nodejs-logging-with-consolelog-buffering/). It is also quickly verbose especially when doing updates and inserts . We could using the programmatic API and define a log to noop but we are still interested for example in getting the other log traces (to know for example the steps and the state of the run) and we would rather use the CLI.

I managed to get to the result by commenting out this line https://github.com/salsita/node-pg-migrate/blob/966883f41227fdb1ecda7d73454aa02217063ca7/src/migration.ts#L155. So I propose to add a `--no-logging` option to the CLI that can take (for now) the _query_ value. It would then disable logging only for queries:

```bash
node-pg-migrate up --no-logging query
```

Happy to send a PR if you were to consider this 😀

Pull request:
Ability to switch off debug logging
Fixes #578 


Patch:
diff --git a/bin/node-pg-migrate b/bin/node-pg-migrate
index 34b6a47..8c65305 100755
--- a/bin/node-pg-migrate
+++ b/bin/node-pg-migrate
@@ -41,6 +41,7 @@ const dryRunArg = 'dry-run'
 const fakeArg = 'fake'
 const decamelizeArg = 'decamelize'
 const tsconfigArg = 'tsconfig'
+const verboseArg = 'verbose'
 
 const { argv } = yargs
   .usage('Usage: $0 [up|down|create|redo] [migrationName] [options]')
@@ -106,6 +107,12 @@ const { argv } = yargs
     type: 'boolean',
   })
 
+  .option(verboseArg, {
+    default: true,
+    describe: 'Print debug messages - all DB statements run',
+    type: 'boolean',
+  })
+
   .option(ignorePatternArg, {
     default: '\\..*',
     describe: 'Regex pattern for file names to ignore',
@@ -199,6 +206,7 @@ let CREATE_MIGRATIONS_SCHEMA = argv[createMigrationsSchemaArg]
 let MIGRATIONS_TABLE = argv[migrationsTableArg]
 let MIGRATIONS_FILE_LANGUAGE = argv[migrationFileLanguageArg]
 let CHECK_ORDER = argv[checkOrderArg]
+let VERBOSE = argv[verboseArg]
 let DECAMELIZE = argv[decamelizeArg]
 
 const tsconfigPath = argv[tsconfigArg]
@@ -237,6 +245,7 @@ function readJson(json) {
     MIGRATIONS_TABLE = json[migrationsTableArg] || MIGRATIONS_TABLE
     MIGRATIONS_FILE_LANGUAGE = json[migrationFileLanguageArg] || MIGRATIONS_FILE_LANGUAGE
     CHECK_ORDER = typeof json[checkOrderArg] !== 'undefined' ? json[checkOrderArg] : CHECK_ORDER
+    VERBOSE = typeof json[verboseArg] !== 'undefined' ? json[verboseArg] : VERBOSE
     IGNORE_PATTERN = json[ignorePatternArg] || IGNORE_PATTERN
     DECAMELIZE = typeof json[decamelizeArg] !== 'undefined' ? json[decamelizeArg] : DECAMELIZE
     if (json.url) {
@@ -353,6 +362,7 @@ if (action === 'create') {
       timestamp,
       file: migrationName,
       checkOrder: CHECK_ORDER,
+      verbose: VERBOSE,
       createSchema: CREATE_SCHEMA,
       createMigrationsSchema: CREATE_MIGRATIONS_SCHEMA,
       direction,
diff --git a/docs/api.md b/docs/api.md
index b53f10d..bbf9f6f 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -23,4 +23,5 @@ which takes options argument with following structure (similar to [command line
 - `dryRun` _[boolean]_
 - `log` _[function]_ - Redirect log messages to this function, rather than `console`
 - `logger` _[object with debug/info/warn/error methods]_ - Redirect messages to this logger object, rather than `console`
+- `verbose` _[boolean]_ - Print all debug messages like DB queries run (if you switch it on, it will disable `logger.debug` method)
 - `decamelize` _[boolean]_ - Runs [`decamelize`](https://github.com/sindresorhus/decamelize) on table/column/etc. names
diff --git a/docs/cli.md b/docs/cli.md
index 68dacb9..86142ca 100644
--- a/docs/cli.md
+++ b/docs/cli.md
@@ -79,6 +79,7 @@ You can adjust defaults by passing arguments to `node-pg-migrate`:
 - `no-lock` - Disables locking mechanism and checks (useful for DBs which does not support SQL commands used for [locking](migrations.md#locking))
 - `fake` - Mark migrations as run without actually performing them (use with caution!)
 - `decamelize` - Runs [`decamelize`](https://github.com/sindresorhus/decamelize) on table/column/etc. names
+- `verbose` - Print all debug messages like DB queries run (defaults to `true`, to switch it off supply `--no-verbose` on command line)
 
 See all by running `node-pg-migrate --help`.
 
diff --git a/src/db.ts b/src/db.ts
index 3822a46..74fba06 100644
--- a/src/db.ts
+++ b/src/db.ts
@@ -1,7 +1,7 @@
 /*
  This file just manages the database connection and provides a query method
  */
-
+import { inspect } from 'util'
 import { Client, ClientConfig, QueryArrayResult, QueryResult, QueryArrayConfig, QueryConfig } from 'pg'
 import { Logger, DB } from './types'
 
@@ -30,7 +30,7 @@ const db = (connection: Client | string | ClientConfig, logger: Logger = console
         ? resolve()
         : client.connect((err) => {
             if (err) {
-              logger.error('could not connect to postgres', err)
+              logger.error(`could not connect to postgres: ${inspect(err)}`)
               return reject(err)
             }
             clientActive = true
diff --git a/src/migration.ts b/src/migration.ts
index 8b746aa..f7d4aaf 100644
--- a/src/migration.ts
+++ b/src/migration.ts
@@ -152,7 +152,9 @@ export class Migration implements RunMigration {
       this.logger.warn('#> WARNING: This migration is not wrapped in a transaction! <')
     }
 
-    this.logger.debug(`${sqlSteps.join('\n')}\n\n`)
+    if (typeof this.logger.debug === 'function') {
+      this.logger.debug(`${sqlSteps.join('\n')}\n\n`)
+    }
 
     return sqlSteps.reduce(
       (promise: Promise<unknown>, sql) => promise.then((): unknown => this.options.dryRun || this.db.query(sql)),
diff --git a/src/runner.ts b/src/runner.ts
index f0b74ab..fcd9a20 100644
--- a/src/runner.ts
+++ b/src/runner.ts
@@ -144,10 +144,14 @@ const runMigrations = (toRun: Migration[], method: 'markAsRun' | 'apply', direct
     Promise.resolve(),
   )
 
-const getLogger = ({ log, logger }: RunnerOption): Logger => {
-  if (typeof logger === 'object') return logger
-  if (typeof log === 'function') return { debug: log, info: log, warn: log, error: log }
-  return console
+const getLogger = ({ log, logger, verbose }: RunnerOption): Logger => {
+  let loggerObject: Logger = console
+  if (typeof logger === 'object') {
+    loggerObject = logger
+  } else if (typeof log === 'function') {
+    loggerObject = { debug: log, info: log, warn: log, error: log }
+  }
+  return verbose ? loggerObject : { ...loggerObject, debug: undefined }
 }
 
 export default async (options: RunnerOption): Promise<RunMigration[]> => {
diff --git a/src/types.ts b/src/types.ts
index ef7f1bf..92bb8d7 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -192,8 +192,8 @@ export enum PgType { // eslint-disable-line import/prefer-default-export
 
 export type MigrationDirection = 'up' | 'down'
 
-export type Logger = Pick<typeof console, 'debug' | 'info' | 'warn' | 'error'>
 export type LogFn = (msg: string) => void
+export type Logger = { debug?: LogFn; info: LogFn; warn: LogFn; error: LogFn }
 
 export interface RunnerOptionConfig {
   migrationsTable: string
@@ -215,6 +215,7 @@ export interface RunnerOptionConfig {
   decamelize?: boolean
   log?: LogFn
   logger?: Logger
+  verbose?: boolean
 }
 
 export interface ConnectionConfig {


Test patch:
diff --git a/test/migration-test.ts b/test/migration-test.ts
index 8bc97b8..63018ed 100644
--- a/test/migration-test.ts
+++ b/test/migration-test.ts
@@ -14,7 +14,7 @@ const actionsPromise = require(`./${promiseMigration}`) // eslint-disable-line i
 
 describe('lib/migration', () => {
   const dbMock = {} as DBConnection
-  const logger: Logger = { debug: () => null, info: () => null, warn: () => null, error: () => null }
+  const logger: Logger = { info: () => null, warn: () => null, error: () => null }
   const options = { migrationsTable } as RunnerOption
   let migration
   let queryMock: SinonSpy