Repository: pinojs/hapi-pino
Pull request #148 (2021-11-08 10:46:54)

Add type declarations
@types/hapi-pino is not compatible with pino's type declarations: [https://github.com/pinojs/pino/blob/38ff645e7fd1a62ccffaa710070a03ba71495f1e/pino.d.ts](url)

The goal of this PR is to make @types/hapi-pino unnecessary.

coveralls: 
[![Coverage Status](https://coveralls.io/builds/44088620/badge)](https://coveralls.io/builds/44088620)

Coverage decreased (-99.0%) to 0.0% when pulling **a1cc41e0f9d217bc863af923d9db4ec3ab8da5ca on lukeg90:types-declaration** into **204c96e0a248c899147243b7a22414eb02edf943 on pinojs:master**.

lukeg90: I added a types test, but I have not added any new API, just documented what already exists. So I'm not sure what would have made the test coverage decrease to 0.
lauraseidler: Great idea, just one comment from my side - should the type tests maybe run in CI as well? I see you added an npm script already, but so far looks like it needs to be run manually, which is easily forgotten :)
lukeg90: Done :) 
arty-name: Please, it would be nice to see this released. [The latest release of pino](https://github.com/pinojs/pino/releases/tag/v7.5.0) made it nigh impossible to use the existing typings of hapi-pino.

Patch:
diff --git a/index.d.ts b/index.d.ts
new file mode 100644
index 0000000..c3fe877
--- /dev/null
+++ b/index.d.ts
@@ -0,0 +1,62 @@
+// Type definitions for hapi-pino 9.0
+// Definitions by: Rodrigo Saboya <https://github.com/saboya>
+//                 Todd Bealmear <https://github.com/todd>
+//                 Matt Jeanes <https://github.com/BlooJeans>
+//                 Kyle Gray <https://github.com/GoPro16>
+// TypeScript Version: 2.8
+
+/// <reference types='node' />
+
+import type { pino } from 'pino';
+
+import { Plugin, Request } from '@hapi/hapi';
+
+declare module '@hapi/hapi' {
+  interface Server {
+    logger: pino.Logger;
+  }
+
+  interface Request {
+    logger: pino.Logger;
+  }
+}
+
+declare namespace HapiPino {
+  interface Serializers {
+    [key: string]: pino.SerializerFn;
+  }
+
+  interface Options {
+    timestamp?: boolean | (() => string) | undefined;
+    logQueryParams?: boolean | undefined;
+    logPayload?: boolean | undefined;
+    logRouteTags?: boolean | undefined;
+    logRequestStart?: boolean | ((req: Request) => boolean) | undefined;
+    logRequestComplete?: boolean | ((req: Request) => boolean) | undefined;
+    stream?: NodeJS.WriteStream | undefined;
+    prettyPrint?: boolean | pino.PrettyOptions | undefined;
+    tags?: { [key in pino.Level]?: string } | undefined;
+    allTags?: pino.Level | undefined;
+    serializers?: Serializers | undefined;
+    getChildBindings?:
+      | ((req: Request) => {
+          level?: pino.Level | string | undefined;
+          serializers?: Serializers | undefined;
+          [key: string]: any;
+        })
+      | undefined;
+    instance?: pino.Logger | undefined;
+    logEvents?: string[] | false | null | undefined;
+    mergeHapiLogData?: boolean | undefined;
+    ignorePaths?: string[] | undefined;
+    level?: pino.Level | undefined;
+    redact?: string[] | pino.redactOptions | undefined;
+    ignoreTags?: string[] | undefined;
+    ignoreFunc?: ((options: Options, request: Request) => boolean) | undefined;
+    ignoredEventTags?: object[] | undefined;
+  }
+}
+
+declare var HapiPino: Plugin<HapiPino.Options>;
+
+export = HapiPino;
diff --git a/index.js b/index.js
index 8257c2b..de6170a 100644
--- a/index.js
+++ b/index.js
@@ -118,7 +118,7 @@ async function register (server, options) {
 
   server.events.on('log', function (event) {
     if (event.error) {
-      logger.error({ err: event.error })
+      logger.error({ tags: event.tags, err: event.error })
     } else if (!isCustomTagsLoggingIgnored(event, ignoredEventTags.log)) {
       logEvent(logger, event)
     }
@@ -142,6 +142,7 @@ async function register (server, options) {
     if (event.error && isEnabledLogEvent(options, 'request-error')) {
       request.logger.error(
         {
+          tags: event.tags,
           err: event.error
         },
         'request error'
diff --git a/package.json b/package.json
index b783dd1..0e9646e 100644
--- a/package.json
+++ b/package.json
@@ -3,11 +3,12 @@
   "version": "9.0.0",
   "description": "Hapi plugin for the Pino logger ",
   "main": "index.js",
+  "types": "index.d.ts",
   "scripts": {
     "coverage": "lab test.js -c",
     "coverage:lcov": "lab test.js -r lcov -o coverage/lcov.info",
     "coveralls": "lab test.js -r lcov | coveralls",
-    "test": "standard && lab --timeout 100000 test.js"
+    "test": "standard && lab --timeout 100000 test.js && tsd"
   },
   "keywords": [
     "hapi",
@@ -25,16 +26,18 @@
     "coveralls": "^3.0.11",
     "flush-write-stream": "^2.0.0",
     "make-promises-safe": "^5.1.0",
+    "pino-pretty": "^7.2.0",
     "pre-commit": "^1.2.2",
     "split2": "^3.1.1",
-    "standard": "^14.3.3"
+    "standard": "^14.3.3",
+    "tsd": "^0.18.0"
   },
   "dependencies": {
     "@hapi/hoek": "^9.0.0",
+    "@types/hapi__hapi": "^20.0.9",
     "abstract-logging": "^2.0.0",
     "get-caller-file": "^2.0.5",
-    "pino": "^7.0.0",
-    "pino-pretty": "^7.2.0"
+    "pino": "^7.0.0"
   },
   "repository": {
     "type": "git",


Test patch:
diff --git a/index.test-d.ts b/index.test-d.ts
new file mode 100644
index 0000000..2612435
--- /dev/null
+++ b/index.test-d.ts
@@ -0,0 +1,45 @@
+import { Request, Server } from '@hapi/hapi';
+import pino from 'pino';
+import * as HapiPino from '.';
+import { expectType } from 'tsd';
+
+const pinoLogger = pino();
+
+const server = new Server();
+
+const options: HapiPino.Options = {
+  timestamp: () => `,"time":"${new Date(Date.now()).toISOString()}"`,
+  logQueryParams: false,
+  logPayload: false,
+  logRouteTags: false,
+  logRequestStart: false,
+  logRequestComplete: true,
+  stream: process.stdout,
+  prettyPrint: process.env.NODE_ENV !== 'PRODUCTION',
+  tags: {
+    trace: 'trace',
+    debug: 'debug',
+    info: 'info',
+    warn: 'warn',
+    error: 'error',
+    fatal: 'fatal',
+  },
+  allTags: 'info',
+  serializers: {
+    req: (req: any) => console.log(req),
+  },
+  getChildBindings: (req: Request) => ({
+    'x-request-id': req.headers['x-request-id'],
+  }),
+  instance: pinoLogger,
+  logEvents: false,
+  mergeHapiLogData: false,
+  ignorePaths: ['/testRoute'],
+  level: 'debug',
+  redact: ['test.property'],
+  ignoreTags: ['healthcheck'],
+  ignoreFunc: (options, request) => request.path.startsWith('/static'),
+  ignoredEventTags: [{ log: ['DEBUG', 'TEST'], request: ['DEBUG', 'TEST'] }],
+};
+
+expectType<Promise<void>>(server.register({ plugin: HapiPino, options }));
diff --git a/test.js b/test.js
index 7b3fa30..aa3d5f0 100644
--- a/test.js
+++ b/test.js
@@ -426,6 +426,7 @@ experiment('logs through server.log', () => {
     })
 
     await tagsWithSink(server, {}, data => {
+      expect(data.tags).to.equal(['error', 'tag'])
       expect(data.err.type).to.equal('Error')
       expect(data.err.message).to.equal('foobar')
       expect(data.err.stack).to.exist()
@@ -434,7 +435,7 @@ experiment('logs through server.log', () => {
       resolver()
     })
 
-    server.log(['error'], new Error('foobar'))
+    server.log(['error', 'tag'], new Error('foobar'))
     await done
   })
 
@@ -593,6 +594,37 @@ experiment('logs through request.log', () => {
     await done
   })
 
+  test('with logged error object', async () => {
+    const server = getServer()
+    server.route({
+      path: '/',
+      method: 'GET',
+      handler: (req, h) => {
+        req.log(['error', 'tag'], new Error('foobar'))
+        return 'hello world'
+      }
+    })
+
+    let resolver
+    const done = new Promise((resolve, reject) => {
+      resolver = resolve
+    })
+
+    await tagsWithSink(server, {}, (data) => {
+      expect(data.tags).to.equal(['error', 'tag'])
+      expect(data.err.type).to.equal('Error')
+      expect(data.err.message).to.equal('foobar')
+      expect(data.err.stack).to.exist()
+      // highest level tag
+      expect(data.level).to.equal(50)
+
+      resolver()
+    })
+
+    await server.inject('/')
+    await done
+  })
+
   test('uses default tag mapping', async () => {
     const server = getServer()
     let done