{"id":13456487,"url":"https://github.com/cabinjs/cabin","last_synced_at":"2025-05-14T08:07:24.750Z","repository":{"id":8613451,"uuid":"10254341","full_name":"cabinjs/cabin","owner":"cabinjs","description":":evergreen_tree: Cabin is the best self-hosted JavaScript and Node.js logging service.  Made for @forwardemail.","archived":false,"fork":false,"pushed_at":"2024-11-26T09:57:04.000Z","size":5052,"stargazers_count":887,"open_issues_count":0,"forks_count":44,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-04-11T19:58:46.385Z","etag":null,"topics":["airbrake","bugsnag","bunyan","cabin","debug","express","inspect","koa","logging","logs","newrelic","papertrail","raven","react","react-native","sentry","service","timber","utility","winston"],"latest_commit_sha":null,"homepage":"https://forwardemail.net/docs/node-js-logging-service","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cabinjs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"niftylettuce","patreon":"niftylettuce"}},"created_at":"2013-05-23T21:54:15.000Z","updated_at":"2025-02-24T17:19:02.000Z","dependencies_parsed_at":"2024-01-16T04:29:58.493Z","dependency_job_id":"6c3d34bf-85fa-4ec7-98f0-3c05087490ef","html_url":"https://github.com/cabinjs/cabin","commit_stats":{"total_commits":286,"total_committers":5,"mean_commits":57.2,"dds":0.2902097902097902,"last_synced_commit":"b803c3798cc7023ccc9b45a2f24e45b30c06df19"},"previous_names":[],"tags_count":108,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cabinjs%2Fcabin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cabinjs%2Fcabin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cabinjs%2Fcabin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cabinjs%2Fcabin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cabinjs","download_url":"https://codeload.github.com/cabinjs/cabin/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254101555,"owners_count":22014908,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["airbrake","bugsnag","bunyan","cabin","debug","express","inspect","koa","logging","logs","newrelic","papertrail","raven","react","react-native","sentry","service","timber","utility","winston"],"created_at":"2024-07-31T08:01:22.915Z","updated_at":"2025-05-14T08:07:19.741Z","avatar_url":"https://github.com/cabinjs.png","language":"JavaScript","readme":"\u003ch1 align=\"center\"\u003e\n  \u003ca href=\"http://cabinjs.com\"\u003e\u003cimg src=\"https://d1i8ikybhfrv4r.cloudfront.net/cabin-animated.gif\" alt=\"cabin\" /\u003e\u003c/a\u003e\n\u003c/h1\u003e\n\u003cdiv align=\"center\"\u003e\n  \u003ca href=\"https://github.com/cabinjs/cabin/actions/workflows/ci.yml\"\u003e\u003cimg src=\"https://github.com/cabinjs/cabin/actions/workflows/ci.yml/badge.svg\" alt=\"build status\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/sindresorhus/xo\"\u003e\u003cimg src=\"https://img.shields.io/badge/code_style-XO-5ed9c7.svg\" alt=\"code style\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/prettier/prettier\"\u003e\u003cimg src=\"https://img.shields.io/badge/styled_with-prettier-ff69b4.svg\" alt=\"styled with prettier\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://lass.js.org\"\u003e\u003cimg src=\"https://img.shields.io/badge/made_with-lass-95CC28.svg\" alt=\"made with lass\" /\u003e\u003c/a\u003e\n  \u003ca href=\"LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/github/license/cabinjs/cabin.svg\" alt=\"license\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://npm.im/cabin\"\u003e\u003cimg src=\"https://img.shields.io/npm/dt/cabin.svg\" alt=\"npm downloads\" /\u003e\u003c/a\u003e\n\u003c/div\u003e\n\u003cbr /\u003e\n\u003cdiv align=\"center\"\u003e\n  Cabin is the best self-hosted \u003ca href=\"https://en.wikipedia.org/wiki/JavaScript\" target=\"_blank\"\u003eJavaScript\u003c/a\u003e and \u003ca href=\"https://nodejs.org\" target=\"_blank\"\u003eNode.js\u003c/a\u003e \u003ca href=\"https://en.wikipedia.org/wiki/Logging_as_a_service\" target=\"_blank\"\u003elogging service\u003c/a\u003e.\n  \u003cbr /\u003e\n  \u003csmall\u003eSupports Node v18+, Browser Environments, \u003ca href=\"https://expressjs.com\" target=\"_blank\"\u003eExpress\u003c/a\u003e, \u003ca href=\"https://koajs.com\" target=\"_blank\"\u003eKoa\u003c/a\u003e, and \u003ca href=\"https://lad.js.org\" target=\"_blank\"\u003eLad\u003c/a\u003e\u003c/small\u003e\n\u003c/div\u003e\n\u003chr /\u003e\n\u003cdiv align=\"center\"\u003e\n  Cabin is compatible with \u003ca href=\"https://sentry.io\" target=\"_blank\"\u003eSentry\u003c/a\u003e, \u003ca href=\"https://airbrake.io/\" target=\"_blank\"\u003eAirbrake\u003c/a\u003e, \u003ca href=\"https://papertrailapp.com/\" target=\"_blank\"\u003ePapertrail\u003c/a\u003e, \u003ca href=\"https://www.loggly.com/\" target=\"_blank\"\u003eLoggly\u003c/a\u003e, and \u003ca href=\"https://www.bugsnag.com/\" target=\"_blank\"\u003eBugsnag\u003c/a\u003e.\n\u003c/div\u003e\n\u003chr /\u003e\n\n\n## Table of Contents\n\n* [Foreword](#foreword)\n* [Quick Start](#quick-start)\n* [Features](#features)\n  * [Security, Privacy, and Business Focused](#security-privacy-and-business-focused)\n  * [Reduce Disk Storage Costs](#reduce-disk-storage-costs)\n  * [Cross-Platform and Cross-Browser Compatible](#cross-platform-and-cross-browser-compatible)\n* [Send Logs to an HTTP endpoint](#send-logs-to-an-http-endpoint)\n* [Send Logs to Slack](#send-logs-to-slack)\n* [Send Logs to Sentry](#send-logs-to-sentry)\n* [Send Logs to Datadog](#send-logs-to-datadog)\n* [Send Logs to Papertrail](#send-logs-to-papertrail)\n* [Suppress Logger Data](#suppress-logger-data)\n* [Install](#install)\n* [Usage](#usage)\n  * [Logging](#logging)\n  * [Route Logging Middleware](#route-logging-middleware)\n  * [Node](#node)\n  * [Browser](#browser)\n  * [Automatic Request Logging](#automatic-request-logging)\n  * [Stack Traces and Error Handling](#stack-traces-and-error-handling)\n* [Options](#options)\n* [Display Metadata and Stack Traces](#display-metadata-and-stack-traces)\n* [Related](#related)\n* [License](#license)\n\n\n## Foreword\n\nPlease defer to Axe's [Foreword](https://github.com/cabinjs/axe/#foreword) for more insight.\n\nCabin is a layer on top of Axe that provides automatic logging for route middleware requests and errors.\n\n\n## Quick Start\n\n```sh\nnpm install express axe cabin signale\n```\n\n```js\nconst express = require('express');\nconst Axe = require('axe');\nconst Cabin = require('cabin');\nconst app = express();\nconst { Signale } = require('signale');\n\n// initialize a new instance of Axe (see below TODO's that appeal to you)\nconst logger = new Axe({\n  logger: new Signale()\n});\n\n// TODO: if you want to send logs to an HTTP endpoint then follow this guide:\n// https://github.com/cabinjs/axe/#send-logs-to-http-endpoint\n\n// TODO: if you want to send logs to Slack then follow this guide:\n// https://github.com/cabinjs/axe/#send-logs-to-slack\n\n// TODO: if you want to send logs to Sentry then follow this guide:\n// https://github.com/cabinjs/axe/#send-logs-to-sentry\n\n// TODO: if you want to send logs to Datadog then follow this guide:\n// https://github.com/cabinjs/axe/#send-logs-to-datadog\n\n// TODO: if you want to send logs to Papertrail then follow this guide:\n// https://github.com/cabinjs/axe/#send-logs-to-papertrail\n\n// TODO: if you want to suppress specific log metadata then follow this guide:\n// https://github.com/cabinjs/axe/#suppress-logger-data\n\n// initialize a new instance of Cabin with an Axe logger instance\nconst cabin = new Cabin({ logger });\n\n//\n// initialize route logging middleware\n//\n// NOTE: this will automatically log route middleware requests and errors\n//\napp.use(cabin.middleware);\n\napp.get('/', (req, res, next) =\u003e res.send('OK'));\n\n// start the server\napp.listen(3000);\n```\n\n```sh\ncurl http://localhost:3000\n```\n\n\n## Features\n\n### Security, Privacy, and Business Focused\n\nCabin will automatically detect and mask the following list of extremely sensitive types of data in your logs:\n\n* [1600+ Sensitive Field Names][sensitive-fields]\n* Credit Card Numbers\u003csup\u003e\\*\u003c/sup\u003e\n* [BasicAuth Headers][basicauth-headers]\n* Social Security Numbers\n* [JSON Web Tokens (\"JWT\")][jwt-tokens]\n* API Keys, CSRF Tokens, and Stripe Tokens\n* Passwords, Salts, and Hashes\n* Bank Account Numbers and Bank Routing Numbers\n\n\u003e \u003csmall\u003e\u003csup\u003e\\*\u003c/sup\u003eCredit card numbers from the following providers are automatically detected and masked: Visa, Mastercard, American Express, Diners Club, Discover, JCB, UnionPay, Maestro, Mir, Elo, Hiper, Hipercard\u003c/small\u003e\n\n### Reduce Disk Storage Costs\n\nReduce your disk storage costs through Cabin's automatic conversion of Streams, Buffers, and ArrayBuffers to simplified, descriptive-only objects that otherwise would be unreadable (and obviously pollute your log files and disk storage).\n\n\u003e Before:\n\n```json\n{\n  \"request\": {\n    \"body\": {\n      \"file\": {\n        \"type\": \"Buffer\",\n        \"data\": [\n          76,\n          111,\n          114,\n          101,\n          109,\n          32,\n          105,\n          112,\n          115,\n          117,\n          109,\n          32,\n          100,\n          111,\n          108,\n          111,\n          114,\n          32,\n          115,\n          105,\n          116,\n          '...'\n        ]\n      }\n    }\n  }\n}\n```\n\n\u003e After\n\n```json\n{\n  \"request\": {\n    \"body\": {\n      \"file\": {\n        \"type\": \"Buffer\",\n        \"byteLength\": 2787\n      }\n    }\n  }\n}\n```\n\n### Cross-Platform and Cross-Browser Compatible\n\nCabin works with the most popular Node.js HTTP frameworks (e.g. [Express][] and [Koa][]), request body handling packages (e.g. [multer][] and [body-parser][]), and the [passport][] authentication framework.\n\nIt supports **Node v18+** and modern browsers out of the box (its browser-ready bundle **is only 20 KB**).\n\n```sh\nnpx browserslist\n```\n\n```sh\nand_chr 107\nand_ff 106\nand_qq 13.1\nand_uc 13.4\nandroid 107\nchrome 107\nchrome 106\nchrome 105\nedge 107\nedge 106\nedge 105\nfirefox 106\nfirefox 105\nfirefox 102\nios_saf 16.1\nios_saf 16.0\nios_saf 15.6\nios_saf 15.5\nios_saf 14.5-14.8\nkaios 2.5\nop_mini all\nop_mob 64\nopera 91\nopera 90\nsafari 16.1\nsafari 16.0\nsafari 15.6\nsamsung 18.0\nsamsung 17.0\n```\n\n\n## Send Logs to an HTTP endpoint\n\nSee the [Quick Start](#quick-start) section above and our guide at \u003chttps://github.com/cabinjs/axe/#send-logs-to-http-endpoint\u003e.\n\n\n## Send Logs to Slack\n\nSee the [Quick Start](#quick-start) section above and our guide at \u003chttps://github.com/cabinjs/axe/#send-logs-to-slack\u003e.\n\n\n## Send Logs to Sentry\n\nSee the [Quick Start](#quick-start) section above and our guide at \u003chttps://github.com/cabinjs/axe/#send-logs-to-sentry\u003e.\n\n\n## Send Logs to Datadog\n\nSee the [Quick Start](#quick-start) section above and our guide at \u003chttps://github.com/cabinjs/axe/#send-logs-to-datadog\u003e.\n\n\n## Send Logs to Papertrail\n\nSee the [Quick Start](#quick-start) section above and our guide at \u003chttps://github.com/cabinjs/axe/#send-logs-to-papertrail\u003e.\n\n\n## Suppress Logger Data\n\nSee the [Quick Start](#quick-start) section above and our guide at \u003chttps://github.com/cabinjs/axe/#suppress-logger-data\u003e.\n\n\n## Install\n\n**Note that as of v11.0.0 Cabin requires a peer dependency of [Axe][] to be installed.**\n\n[npm][]:\n\n```sh\nnpm install cabin axe\n```\n\n\n## Usage\n\n### Logging\n\n```js\nconst Cabin = require('cabin');\nconst cabin = new Cabin({\n  // ... see the Quick Start and Options sections\n});\n\ncabin.info('hello world');\ncabin.error(new Error('oops!'));\n```\n\n### Route Logging Middleware\n\n```js\napp.use(cabin.middleware);\n```\n\nSee either the [Node](#node) or [Browser](#browser) instructions below for further route middleware usage and proper setup.\n\n### Node\n\n\u003e The examples below show how to use Cabin in combination with [Axe][], [Signale][] (instead of `console`), and how to add an accurate `X-Response-Time` response time metric to your logs and response headers automatically.\n\n#### Koa\n\n1. Install required and recommended dependencies:\n\n   ```sh\n   npm install koa cabin signale request-received koa-better-response-time koa-better-request-id\n   ```\n\n2. Implement the example code below ([also found here](examples/koa.js)):\n\n   ```js\n   const Koa = require('koa');\n   const Cabin = require('cabin');\n   const Router = require('koa-router');\n   const requestReceived = require('request-received');\n   const responseTime = require('koa-better-response-time');\n   const requestId = require('koa-better-request-id');\n   const { Signale } = require('signale');\n\n   const app = new Koa();\n   const router = new Router();\n   const cabin = new Cabin({\n     logger: new Signale()\n   });\n\n   // adds request received hrtime and date symbols to request object\n   // (which is used by Cabin internally to add `request.timestamp` to logs\n   app.use(requestReceived);\n\n   // adds `X-Response-Time` header to responses\n   app.use(responseTime());\n\n   // adds or re-uses `X-Request-Id` header\n   app.use(requestId());\n\n   // use the cabin middleware (adds request-based logging and helpers)\n   app.use(cabin.middleware);\n\n   // add your user/session management middleware here (e.g. passport)\n   // ...\n\n   // an example home page route\n   router.get('/', ctx =\u003e {\n     ctx.logger.info('visited home page');\n     ctx.body = 'hello world';\n   });\n\n   // this assumes that you are using passport which\n   // exposes `ctx.logout` to log out the logged in user\n   router.get('/logout', ctx =\u003e {\n     ctx.logger.warn('Logged out');\n     ctx.logout();\n     ctx.redirect('/');\n   });\n\n   app.use(router.routes());\n   app.use(router.allowedMethods());\n\n   app.listen(3000, () =\u003e {\n     cabin.info('app started');\n   });\n   ```\n\n3. See [Koa convenience methods below](#koa-1) for helper utilities you can use while writing code.\n\n#### Express\n\n1. Install required and recommended dependencies:\n\n   ```sh\n   npm install express cabin signale request-received response-time express-request-id\n   ```\n\n2. Implement the example code below ([also found here](examples/express.js)):\n\n   ```js\n   const express = require('express');\n   const Cabin = require('cabin');\n   const requestReceived = require('request-received');\n   const responseTime = require('response-time');\n   const requestId = require('express-request-id');\n   const { Signale } = require('signale');\n\n   const app = express();\n   const cabin = new Cabin({\n     logger: new Signale()\n   });\n\n   // adds request received hrtime and date symbols to request object\n   // (which is used by Cabin internally to add `request.timestamp` to logs\n   app.use(requestReceived);\n\n   // adds `X-Response-Time` header to responses\n   app.use(responseTime());\n\n   // adds or re-uses `X-Request-Id` header\n   app.use(requestId());\n\n   // use the cabin middleware (adds request-based logging and helpers)\n   app.use(cabin.middleware);\n\n   // add your user/session management middleware here (e.g. passport)\n   // ...\n\n   // an example home page route\n   app.get('/', (req, res) =\u003e {\n     req.logger.info('visited home page');\n     res.send('hello world');\n   });\n\n   // this assumes that you are using passport which\n   // exposes `req.logout` to log out the logged in user\n   app.get('/logout', (req, res) =\u003e {\n     req.logger.warn('logged out');\n     req.logout();\n     res.redirect('/');\n   });\n\n   app.listen(3000, () =\u003e {\n     cabin.info('app started');\n   });\n   ```\n\n3. See [Express convenience methods below](#express-1) for helper utilities you can use while writing code.\n\n#### Convenience Methods\n\nIn order to easily interact and use the `logger` utility function exposed by `app.use(cabin.middleware)`, we expose convenient helper methods in Express and Koa:\n\n##### Express\n\n* `req.log`\n* `req.logger`\n* `res.log`\n* `res.logger`\n\n##### Koa\n\n* `ctx.log`\n* `ctx.logger`\n* `ctx.request.log`\n* `ctx.request.logger`\n* `ctx.response.log`\n* `ctx.response.logger`\n\n### Browser\n\nThis package requires Promise support, therefore you will need to polyfill if you are using an unsupported browser (namely Opera mini).\n\n**We no longer support IE as of Cabin v10.0.0+.**\n\n#### VanillaJS\n\nThis is the solution for you if you're just using `\u003cscript\u003e` tags everywhere!\n\n```html\n\u003cscript src=\"https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js?features=Promise\"\u003e\u003c/script\u003e\n\u003cscript src=\"https://unpkg.com/cabin\"\u003e\u003c/script\u003e\n\u003cscript type=\"text/javascript\"\u003e\n  (function() {\n    var cabin = new Cabin();\n    cabin.setUser({\n      id: '1',\n      email: 'test@example.com',\n      full_name: 'Test'\n    });\n    cabin.info('viewed docs');\n  })();\n\u003c/script\u003e\n```\n\n#### Required Browser Features\n\nWe recommend using \u003chttps://cdnjs.cloudflare.com/polyfill\u003e (specifically with the bundle mentioned in [VanillaJS](#vanillajs) above):\n\n```html\n\u003cscript src=\"https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js?features=Promise\"\u003e\u003c/script\u003e\n```\n\n* Promise is not supported in op\\_mini all\n\n#### Bundler\n\nThis assumes you are using [browserify][], [webpack][], [rollup][], or another bundler.\n\n```js\nconst Cabin = require('cabin');\n\nconst cabin = new Cabin();\n\ncabin.setUser({\n  id: '1',\n  email: 'test@example.com',\n  full_name: 'Test'\n});\n\ncabin.info('viewed docs');\n```\n\n### Automatic Request Logging\n\n#### Server\n\nFor server-side logging of requests, the Cabin middleware `cabin.middleware` will automatically log requests for you upon completion.  Just make sure you are using `express-request-id` middleware like in the examples above in order for the `X-Request-Id` header to be set (and re-used if already exists, e.g. generated from client side as in below).  If you're using Koa make sure to use `koa-better-request-id` as shown in the examples above.\n\n#### Browser\n\nWe strongly recommend that you implement one of the following code snippets with [xhook][] (for either VanillaJS or Bundler approaches) so that all your XHR requests have a `X-Request-Id` automatically added (which in turn ensures both client and server have matching request ID's).  Imagine how awesome your logs will be when you can see the full trace starting with the client!\n\n##### HTML\n\n```html\n\u003cscript src=\"https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js?features=Promise\"\u003e\u003c/script\u003e\n\u003cscript src=\"https://unpkg.com/xhook\"\u003e\u003c/script\u003e\n\u003cscript src=\"https://unpkg.com/cabin\"\u003e\u003c/script\u003e\n\u003cscript src=\"https://unpkg.com/parse-request\"\u003e\u003c/script\u003e\n\u003cscript src=\"https://unpkg.com/cuid\"\u003e\u003c/script\u003e\n\u003cscript\u003e\n  (function() {\n    var cabin = new Cabin();\n    cabin.setUser({\n      id: '1',\n      email: 'test@example.com',\n      full_name: 'Test'\n    });\n    xhook.before(function(req) {\n      if (typeof req.headers !== 'object') req.headers = {};\n\n      if (!req.headers['X-Request-Id'])\n        req.headers['X-Request-Id'] = cuid();\n\n      if (!req.headers['User-Agent'])\n        req.headers['User-Agent'] = window.navigator.userAgent;\n\n      if (!req.headers['Referer'])\n        req.headers['Referer'] = window.document.referrer;\n\n      if (!req.headers['Cookie'])\n        req.headers['Cookie'] = window.document.cookie;\n\n      cabin.info('xhr', parseRequest({ req: req }));\n    });\n  })();\n\u003c/script\u003e\n```\n\n##### Pug\n\n\u003e You can do a similar approach with React, EJS, or another templating language.\n\n```pug\nscript(src='https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js?features=Promise')\nscript(src='https://unpkg.com/xhook')\nscript(src='https://unpkg.com/cabin')\nscript(src='https://unpkg.com/parse-request')\nscript(src='https://unpkg.com/cuid')\nscript.\n  (function() {\n    var cabin = new Cabin();\n    cabin.setUser({\n      id: '1',\n      email: 'test@example.com',\n      full_name: 'Test'\n    });\n    xhook.before(function(req) {\n      if (typeof req.headers !== 'object') req.headers = {};\n\n      if (!req.headers['X-Request-Id'])\n        req.headers['X-Request-Id'] = cuid();\n\n      if (!req.headers['X-Request-Id'])\n        req.headers['X-Request-Id'] = cuid();\n\n      if (!req.headers['User-Agent'])\n        req.headers['User-Agent'] = window.navigator.userAgent;\n\n      if (!req.headers['Referer'])\n        req.headers['Referer'] = window.document.referrer;\n\n      if (!req.headers['Cookie'])\n        req.headers['Cookie'] = window.document.cookie;\n\n      cabin.info('xhr', parseRequest({ req: req }));\n    });\n  })();\n```\n\n##### Bundler\n\n[npm][]:\n\n```sh\nnpm install cabin xhook cuid\n```\n\n```js\nconst Cabin = require('cabin');\nconst xhook = require('xhook');\nconst parseRequest = require('parse-request');\nconst cuid = require('cuid');\n\nconst cabin = new Cabin();\n\ncabin.setUser({\n  id: '1',\n  email: 'test@example.com',\n  full_name: 'Test'\n});\n\nxhook.before(req =\u003e {\n  if (typeof req.headers !== 'object') req.headers = {};\n\n  if (!req.headers['X-Request-Id'])\n    req.headers['X-Request-Id'] = cuid();\n\n  //\n  // NOTE: you may want to add User-Agent, Referer, and Cookie (see above)\n  //\n  cabin.info('xhr', parseRequest({ req: req }));\n});\n```\n\n### Stack Traces and Error Handling\n\nWe leave it up to you to decide how you wish to handle stack traces and errors, but we've documented our approaches for Node and Browser environments below.\n\n#### Node\n\nIf you're using [Lad][], then you don't need to worry about error handling, as it's built-in (complete with graceful reloading, even for database connections).\n\nHowever you can otherwise use a tool such as [uncaught][] to listen for errors, or bind purely to `process` events emitted as shown below:\n\n```js\nconst Cabin = require('cabin');\n\nconst cabin = new Cabin();\n\nprocess.on('uncaughtException', err =\u003e {\n  cabin.error(err);\n  process.exit(1);\n});\n\nprocess.on('unhandledRejection', err =\u003e {\n  cabin.error(err);\n});\n```\n\n#### Browser\n\nSince cross-browser support is very limited and non-standardized for errors and stack traces, we highly recommend to use [StackTrace](#stacktrace).\n\n##### StackTrace\n\nWe recommend to use [StackTrace][] instead of [TraceKit][tracekit] as it is a more modern alternative and provides much similarity between your Browser and your Node errors (stackframes are basically similar to representations in Gecko and V8, aka the ones you get with Node).\n\nIt does require you to have a polyfill if you're using it in the browser (only if you're supporting browsers that don't support standardized Promises/JSON).  You'll basically need `es6-promise` and `json3` polyfills for browsers you wish to support that don't have them.  The example below shows you how to polyfill, don't worry!  You can reference Caniuse data on [Promises][] and [JSON][] respectively if you need.\n\nThe example below demonstrates using StackTrace with [uncaught][] to catch global errors below.\n\n```html\n\u003cscript src=\"https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js?features=Promise\"\u003e\u003c/script\u003e\n\u003cscript src=\"https://unpkg.com/stackframe\"\u003e\u003c/script\u003e\n\u003cscript src=\"https://unpkg.com/stacktrace-js\"\u003e\u003c/script\u003e\n\u003cscript src=\"https://unpkg.com/uncaught\"\u003e\u003c/script\u003e\n\u003cscript src=\"https://unpkg.com/cabin\"\u003e\u003c/script\u003e\n\u003cscript src=\"https://unpkg.com/prepare-stack-trace\"\u003e\u003c/script\u003e\n\n\u003cscript type=\"text/javascript\"\u003e\n  (function() {\n    //\n    // Sourced from the StackTrace example from CabinJS docs\n    // \u003chttps://github.com/cabinjs/cabin#stacktrace\u003e\n    //\n    var cabin = new Cabin();\n\n    // Use cabin globally in your app (instead of `console`)\n    window.cabin = cabin;\n\n    // Bind event listeners\n    uncaught.start();\n    uncaught.addListener(function(err, event) {\n      if (!err) {\n        if (typeof ErrorEvent === 'function' \u0026\u0026 event instanceof ErrorEvent)\n          return cabin.error(event.message, { event: event });\n        cabin.error({ event: event });\n        return;\n      }\n      // this will transform the error's `stack` property\n      // to be consistently similar to Gecko and V8 stackframes\n      StackTrace.fromError(err)\n        .then(function(stackframes) {\n          err.stack = prepareStackTrace(err, stackframes);\n          cabin.error(err);\n        })\n        .catch(function(err2) {\n          cabin.error(err);\n          cabin.error(err2);\n        });\n    });\n  })();\n\u003c/script\u003e\n```\n\n\n## Options\n\n* `logger` (Object or [Axe][] instance) - if you have a custom logger you wish to use or an existing [Axe][] instance – defaults to an instance of [Axe][] which uses `console` as the logger – if you do not pass an instance of Axe, then an instance will be created and the `logger` option will be passed down\n* `meta` (Object) - defaults to an empty object - this will get passed as metadata (e.g. you could set a custom `meta.user` object here for every request)\n* `parseRequest` (Object) - defaults to an empty object, which means it will use the defaults from [parse-request][] (see [Metadata](#metadata) below)\n* `errorProps` (Array) - a list of properties to cherry-pick from the error object parsed out of err thanks to [parse-err][] (by default all properties are returned; even non-enumerable ones and ones on the prototype object) (see [Metadata](#metadata) below)\n* `message` (Function) - inspired by [morgan][], and defaults to a [dev-friendly format](https://github.com/expressjs/morgan#short) (or if in production mode, then it uses a [standard Apache common log format][apache-clf])). – when requests finish, it will utilize `logger` to output an error, warn, or info level log based off the status code, and this function is used to determine the string sent to the logger.  It accepts one argument `options`, which is comprised of `options.level`, `options.req`, `options.res`, and optionally (if and only if Koa) `options.ctx`.  It is required that this function return a String.  See [src/message.js](src/message.js) for the default message function.  Note that both dev-friendly and Apache common log formats are stripped of basic auth lines for obvious security reasons.  **Note that if a `null` or `undefined` value is returned from the message function, then the logger will not be invoked unless there is an error.**\n\n\n## Display Metadata and Stack Traces\n\nUnder the hood, Cabin uses [Axe][] which provides us with several options, including one to show metadata (e.g. request headers, body, and user) and another to show stack traces for errors.\n\nTo show/hide application metadata and/or stack traces, see the [Axe options documentation](https://github.com/cabinjs/axe#options).\n\nCabin uses the package [parse-request][] to parse the request metadata for you automatically in your Express and Koa applications.\n\nHere's an example of a parsed metadata object:\n\n```js\n{\n  request: {\n    method: 'POST',\n    query: {\n      foo: 'bar',\n      beep: 'boop'\n    },\n    headers: {\n      host: '127.0.0.1:63955',\n      'accept-encoding': 'gzip, deflate',\n      'user-agent': 'node-superagent/3.8.3',\n      authorization: 'Basic ********************',\n      accept: 'application/json',\n      cookie: 'foo=bar;beep=boop',\n      'content-type': 'multipart/form-data; boundary=--------------------------930511303948232291410214',\n      'content-length': '1599',\n      connection: 'close'\n    },\n    cookies: {\n      foo: 'bar',\n      beep: 'boop'\n    },\n    body: '{\"product_id\":\"5d0350ef2ca74d11ee6e4f00\",\"name\":\"nifty\",\"surname\":\"lettuce\",\"bank_account_number\":\"1234567890\",\"card\":{\"number\":\"****-****-****-****\"},\"stripe_token\":\"***************\",\"favorite_color\":\"green\"}',\n    url: '/?foo=bar\u0026beep=boop',\n    timestamp: '2019-06-14T07:46:55.568Z',\n    id: 'fd6225ed-8db0-4862-8566-0c0ad6f4c7c9',\n    http_version: '1.1',\n    files: '{\"avatar\":[{\"fieldname\":\"avatar\",\"originalname\":\"avatar.png\",\"encoding\":\"7bit\",\"mimetype\":\"image/png\",\"buffer\":{\"type\":\"Buffer\",\"byteLength\":216},\"size\":216}],\"boop\":[{\"fieldname\":\"boop\",\"originalname\":\"boop-1.txt\",\"encoding\":\"7bit\",\"mimetype\":\"text/plain\",\"buffer\":{\"type\":\"Buffer\",\"byteLength\":7},\"size\":7},{\"fieldname\":\"boop\",\"originalname\":\"boop-2.txt\",\"encoding\":\"7bit\",\"mimetype\":\"text/plain\",\"buffer\":{\"type\":\"Buffer\",\"byteLength\":7},\"size\":7}]}'\n  },\n  user: {\n    ip_address: '::ffff:127.0.0.1'\n  },\n  id: '5d0350ef2ca74d11ee6e4f01',\n  timestamp: '2019-06-14T07:46:55.000Z',\n  duration: 6.651317\n}\n```\n\nAs you can see, sensitive data is masked and contextual user information metadata is automatically populated.\n\n\n## Related\n\n* [Forward Email][forward-email] - Free, encrypted, and open-source email forwarding service for custom domains\n* [Axe][] - Logging utility for Node and Browser environments. Chop up your logs!\n* [Bree][] - The best job scheduler for [Node.js][node]\n* [Lad][] - Scaffold a [Koa][] webapp and API framework for [Node.js][node]\n* [Lass][] - Scaffold a modern boilerplate for [Node.js][node]\n* [koa-better-error-handler][] - A better error-handler for Lad and Koa. Makes `ctx.throw` awesome!\n\n\n## License\n\n[MIT](LICENSE) © Titanism\n\n\n##\n\n\u003ca href=\"#\"\u003e\u003cimg src=\"media/cabin-footer.png\" alt=\"#\" /\u003e\u003c/a\u003e\n\n[bree]: https://jobscheduler.net\n\n[npm]: https://www.npmjs.com/\n\n[passport]: http://www.passportjs.org/\n\n[lad]: https://lad.js.org\n\n[lass]: https://lass.js.org\n\n[axe]: https://github.com/cabinjs/axe\n\n[koa]: http://koajs.com/\n\n[node]: https://nodejs.org\n\n[koa-better-error-handler]: https://github.com/ladjs/koa-better-error-handler\n\n[webpack]: https://github.com/webpack/webpack\n\n[rollup]: https://github.com/rollup/rollup\n\n[uncaught]: https://github.com/aleksandr-oleynikov/uncaught\n\n[xhook]: https://github.com/jpillora/xhook\n\n[parse-request]: https://github.com/cabinjs/parse-request\n\n[parse-err]: https://github.com/cabinjs/parse-err\n\n[stacktrace]: https://www.stacktracejs.com/\n\n[tracekit]: https://github.com/csnover/TraceKit\n\n[promises]: https://caniuse.com/#feat=promises\n\n[json]: https://caniuse.com/#feat=json\n\n[signale]: https://github.com/klauscfhq/signale\n\n[sensitive-fields]: https://github.com/cabinjs/sensitive-fields/blob/master/index.json\n\n[basicauth-headers]: https://en.wikipedia.org/wiki/Basic_access_authentication\n\n[jwt-tokens]: https://en.wikipedia.org/wiki/JSON_Web_Token\n\n[express]: https://expressjs.com\n\n[multer]: https://github.com/expressjs/multer\n\n[body-parser]: https://github.com/expressjs/body-parser\n\n[morgan]: https://github.com/expressjs/morgan\n\n[forward-email]: https://forwardemail.net\n\n[apache-clf]: https://github.com/expressjs/morgan#common\n\n[browserify]: https://github.com/browserify/browserify\n","funding_links":["https://github.com/sponsors/niftylettuce","https://patreon.com/niftylettuce"],"categories":["JavaScript","Repository","仓库","Framework agnostic packages"],"sub_categories":["Logging","中间件","Node"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcabinjs%2Fcabin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcabinjs%2Fcabin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcabinjs%2Fcabin/lists"}