docs: function auditLogger(opts) {
      assert.object(opts, 'opts');
      assert.object(opts.log, 'opts.log');
      assert.string(opts.event, 'opts.event');
      assert.optionalObject(opts.server, 'opts.server');
      assert.optionalBool(opts.printLog, 'opts.printLog');
  
      if (opts.event !== 'after' && opts.event !== 'pre' &&
          opts.event !== 'routed') {
          throw new VError('opts.event must be %s, %s, or %s, but is %s', 'pre',
              'routed', 'after', opts.event);
      }
  
      var server = opts.server;
      var printLog = opts.printLog;
  
      if (typeof printLog === 'undefined') {
          printLog = true;
      }
      var errSerializer = bunyan.stdSerializers.err;
  
      if (opts.log.serializers && opts.log.serializers.err) {
          errSerializer = opts.log.serializers.err;
      }
  
      var log = opts.log.child({
          audit: true,
          component: opts.event,
          serializers: {
              err: errSerializer,
              req: function auditRequestSerializer(req) {
                  if (!req) {
                      return (false);
                  }
  
                  var timers = {};
                  (req.timers || []).forEach(function (time) {
                      var t = time.time;
                      var _t = Math.floor((1000000 * t[0]) + (t[1] / 1000));
                      timers[time.name] = _t;
                  });
                  return ({
                      // account for native and queryParser plugin usage
                      query: (typeof req.query === 'function') ?
                              req.query() : req.query,
                      method: req.method,
                      url: req.url,
                      headers: req.headers,
                      httpVersion: req.httpVersion,
                      trailers: req.trailers,
                      version: req.version(),
                      body: opts.body === true ? req.body : undefined,
                      timers: timers,
                      connectionState: (req.connectionState &&
                                       req.connectionState())
                  });
              },
              res: function auditResponseSerializer(res) {
                  if (!res) {
                      return (false);
                  }
  
                  var body;
  
                  if (opts.body === true) {
                      if (res._body instanceof HttpError) {
                          body = res._body.body;
                      } else {
                          body = res._body;
                      }
                  }
  
                  return ({
                      statusCode: res.statusCode,
                      headers: res._headers,
                      trailer: res._trailer || false,
                      body: body
                  });
              }
          }
      });
  
      function audit(req, res, route, err) {
          var latency = res.get('Response-Time');
  
          if (typeof (latency) !== 'number') {
              latency = Date.now() - req._time;
          }
  
          var obj = {
              remoteAddress: req.connection.remoteAddress,
              remotePort: req.connection.remotePort,
              req_id: req.getId(),
              req: req,
              res: res,
              err: err,
              latency: latency,
              secure: req.secure,
              _audit: true,
              event: opts.event
          };
  
          if (printLog) {
              switch (opts.event) {
                  case 'after':
                      log.info(obj, 'handled: %d', res.statusCode);
                      break;
                  case 'pre':
                      log.info(obj, 'pre');
                      break;
                  case 'routed':
                      log.info(obj, 'routed');
                      break;
                  default:
                      throw new Error('Unexpected audit event: ' + opts.event);
              }
          }
  
          if (server) {
              server.emit('audit', obj);
          }
  
          return (true);
      }
  
      return (audit);
  }