{"id":21031295,"url":"https://github.com/workable/riviere","last_synced_at":"2026-02-19T12:05:17.837Z","repository":{"id":22849771,"uuid":"97233194","full_name":"Workable/riviere","owner":"Workable","description":"Koa HTTP traffic and error logger","archived":false,"fork":false,"pushed_at":"2024-08-08T07:14:11.000Z","size":1654,"stargazers_count":12,"open_issues_count":6,"forks_count":2,"subscribers_count":31,"default_branch":"master","last_synced_at":"2024-08-08T16:52:50.718Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Workable.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2017-07-14T12:49:02.000Z","updated_at":"2024-08-08T07:14:11.000Z","dependencies_parsed_at":"2023-09-27T18:16:07.453Z","dependency_job_id":"d2b70db6-ad6f-4b78-bac7-746adbe2ab61","html_url":"https://github.com/Workable/riviere","commit_stats":null,"previous_names":[],"tags_count":74,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Workable%2Friviere","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Workable%2Friviere/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Workable%2Friviere/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Workable%2Friviere/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Workable","download_url":"https://codeload.github.com/Workable/riviere/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225349138,"owners_count":17460315,"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":[],"created_at":"2024-11-19T12:27:08.659Z","updated_at":"2026-02-19T12:05:12.799Z","avatar_url":"https://github.com/Workable.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![NPM](https://nodei.co/npm/@workablehr/riviere.png)](https://nodei.co/npm/@workablehr/riviere)\n\n[![Coverage Status](https://coveralls.io/repos/github/Workable/riviere/badge.svg?branch=master)](https://coveralls.io/github/Workable/riviere?branch=master)\n\n# riviere\n\nThe `riviere` module decorates your `Koa` middleware chain with inbound/outbound HTTP traffic logs.\n\nUse `riviere` if you want an easy way to log all the HTTP traffic for your server.\n`riviere` works independently of your logging library, if you  use any.\n\n---\n\n# Table of contents\n1. [Features](#Features)\n2. [Example logs](#Example_logs)\n3. [Requirements](#Requirements)\n4. [Installation](#Installation)\n5. [Usage](#Usage)\n6. [Configuration](#Configuration)\n7. [Available Options](#Available_options)\n    1. [inbound.enabled](#options_inbound_enabled)\n    2. [inbound.includeHost](#options_inbound_includeHost)\n    3. [inbound.request.enabled](#options_inbound_request_enabled)\n    4. [inbound.maxBodyValueChars](#options_inbound_max_body_value_chars)\n    5. [bodyKeys](#options_body_keys)\n    6. [bodyKeysRegex](#options_body_keys_regex)\n    7. [bodyKeysCallback](#options_body_keys_callback)\n    8. [color](#options_color)\n    9. [styles](#options_styles)\n    10. [context](#options_context)\n    11. [errors.callback](#options_errors_callback)\n    12. [headersRegex](#options_headers_regex)\n    13. [headerValueCallback](#options_header_value_callback)\n    14. [health](#options_health)\n    15. [outbound.enabled](#options_outbound_enabled)\n    16. [outbound.request.enabled](#options_outbound_request_enabled)\n    17. [outbound.maxBodyValueChars](#options_outbound_max_body_value_chars)\n    18. [outbound.blacklistedPathRegex](#options_outbound_blacklisted_path_regex)\n    19. [traceHeaderName](#options_trace_header_name)\n8. [License](#License)\n\n---\n\n\u003ca name=\"Features\"\u003e\u003c/a\u003e\n## Features\n\n- Log all the HTTP(s) requests that are coming into your server and the corresponding responses.\n  (`inbound_request`/`outbound_response`)\n- Log all the HTTP(s) requests that your server sends to any external systems and the corresponding responses.\n  (`outbound_request`/`inbound_response`)\n- Log any unhandled errors that are thrown inside a requests's context.\n- Format logs in json (fully compatible with Google Stackdriver)\n\n---\n\n\u003ca name=\"Example_logs\"\u003e\u003c/a\u003e\n## Example logs\n\n![alt text](https://raw.githubusercontent.com/Workable/riviere/images/images/riviere_logs_screen_5.png)\n\nThe above example logs are generated by executing:\n```js\ncurl localhost:3000 -H \"X-myHeader: myHeaderValue\"\n```\nAssuming that the following example server is running:\n![examples/hello_with_outgoing_traffic.js](https://github.com/Workable/riviere/blob/master/examples/hello_with_outgoing_traffic.js)\n\n---\n\n\u003ca name=\"Requirements\"\u003e\u003c/a\u003e\n## Requirements\n\n* Node version \u003e= 8\n* Koa version \u003e= 2\n\n---\n\n\u003ca name=\"Installation\"\u003e\u003c/a\u003e\n## Installation\n\n```npm i --save riviere```\n\n---\n\n\u003ca name=\"Usage\"\u003e\u003c/a\u003e\n## Usage\n\n*Example*:\n\n```js\nconst Koa = require('koa');\nconst riviere = require('riviere');\n\nconst app = new Koa();\n\napp.use(riviere());\napp.use(async function(ctx) {\n    ctx.body = 'Hello World';\n});\n\napp.listen(3000);\n```\n\n\u003ca name=\"example_with_outbound_http_traffic\"\u003e\u003c/a\u003e\n*Example with outbound HTTP traffic*:\n\n```js\nconst Koa = require('koa');\nconst riviere = require('riviere');\n\n// this is just an example, you can use any http library\nconst rp = require('request-promise');\n\nconst app = new Koa();\n\napp.use(riviere());\napp.use(async function(ctx) {\n  await rp({\n    uri: 'https://www.google.com',\n    // You can include the x-riviere-id header\n    // to trace the request's context inside which\n    // the external request is made\n    // This is optional but recommended for better tracing:\n    headers: {\n      'x-riviere-id': ctx.request.headers['x-riviere-id'] // notice that this is lowercase\n    }\n  });\n  ctx.body = 'Hello World';\n});\n\napp.listen(3000);\n```\n\n---\n\n\u003ca name=\"Configuration\"\u003e\u003c/a\u003e\n## Configuration\n\nThe behavior of the riviere middleware can be configured by passing a configuration object,\nas argument to the `riviere()` method call.\n\nYou can start using `riviere'` by just calling `app.use(riviere())`.\nIn this case the `riviere` will use its default configuration.\n\nThe default configuration object is the following:\n\n```js\nconst configuration = {\n    context: ctx =\u003e {\n        return {};\n    },\n    errors: {\n        enabled: true,\n        callback: (err, ctx) =\u003e {\n            throw(err);\n        }\n    },\n    health: [],\n    inbound: {\n        enabled: true,\n        request: {\n            enabled: true\n        },\n        maxBodyValueChars: undefined\n    },\n    outbound: {\n        enabled: true,\n        request: {\n            enabled: true\n        },\n        maxBodyValueChars: undefined\n    },\n    bodyKeys: [],\n    bodyKeysRegex: undefined,\n    bodyKeysCallback: (body: any, ctx?: KoaContext) =\u003e ({}),\n    headersRegex: new RegExp('^X-.*', 'i'),\n    traceHeaderName: 'X-Riviere-Id',\n    styles: ['simple'],\n}\n```\n\nHere is an example of a more advanced configuration:\n\n```js\nconst configuration = {\n    bodyKeys: [\n        'education',\n        'work_experience'\n    ],\n    color: true,\n    context: (ctx) =\u003e {\n        return {\n            userId: ctx.request.headers['user-id'],\n            accountId: ctx.request.headers['account-id']\n        };\n    },\n    errors: {\n        enabled: true,\n        callback: (ctx, error) =\u003e {\n            ctx.status = error.status || 500;\n\n            if (ctx.status \u003c 500) {\n                ctx.body = {error: error.message};\n            } else {\n                ctx.body = {error: 'Internal server error'};\n            }\n        }\n    },\n    headersRegex: new RegExp('X-.+', 'i'),\n    health: [\n        {\n            path: '/health',\n            method: 'GET'\n        }\n    ],\n    outbound: {\n        enabled: true\n    },\n    traceHeaderName: 'X-Request-Id',\n    styles: ['simple', 'json']\n};\n\napp.use(riviere(configuration));\n```\n\nThe supported key-value options, for the configuration object are described below.\n\n---\n\n\u003ca name=\"Available_options\"\u003e\u003c/a\u003e\n### Available options\n\n\u003ca name=\"options_inbound\"\u003e\u003c/a\u003e\n**inbound**\n\n\u003ca name=\"options_inbound_enabled\"\u003e\u003c/a\u003e\n**inbound.enabled**\n\nEnable inbound HTTP traffic logging. Defaults to `true`.\n\n\u003ca name=\"options_inbound_request_enabled\"\u003e\u003c/a\u003e\n**inbound.request.enabled**\n\nEnable inbound_request HTTP traffic logging. Defaults to `true`.\n\n\u003ca name=\"options_inbound_includeHost\"\u003e\u003c/a\u003e\n**inbound.includeHost**\n\nLog full path, including host on inbound requests. Defaults to `false`.\n\n\u003ca name=\"options_inbound_max_body_value_chars\"\u003e\u003c/a\u003e\n**inbound.maxBodyValueChars**\n\nThis option can be used to truncate values `JSON` body of the `inbound` `POST` requests to a specified length. Defaults to undefined.\nTo use this option, the `POST` request's body should be a valid `JSON`.\n\n*Example*:\n\n```js\n{\n    inbound: {\n        maxBodyValueChars: 1024\n    }\n}\n```\n\n\u003ca name=\"options_body_keys\"\u003e\u003c/a\u003e\n**bodyKeys**\n\nThis option can be used to log specific values from the `JSON` body of the `inbound` `POST`, `PUT` \u0026 `PATCH` requests.\nDefaults to empty Array `[]`.\nTo use this option, the request's body should be a valid `JSON`.\nMost often this mean that you should register the `Koa` `bodyParser` middleware\n(https://www.npmjs.com/package/body-parser) (or something equivalent),\nbefore registering the `riviere` middleware.\n\n*Example*:\n\n```js\n{\n    bodyKeys: [\n        'education',\n        'work_experience'\n    ]\n}\n```\n\n\u003ca name=\"options_body_keys_regex\"\u003e\u003c/a\u003e\n**bodyKeysRegex**\n\nThis option can be used to log specific values from the `JSON` body of the `inbound` `POST`, `PUT` \u0026 `PATCH` requests.\nDefaults to undefined.\nTo use this option, the request's body should be a valid `JSON`.\nMost often this mean that you should register the `Koa` `bodyParser` middleware\n(https://www.npmjs.com/package/body-parser) (or something equivalent),\nbefore registering the `riviere` middleware.\n\nThis option will override `bodyKeys`\n\n*Example*:\n\n```js\n{\n    bodyKeysRegex: new RegExp('.*');\n}\n```\n\n\u003ca name=\"options_body_keys_callback\"\u003e\u003c/a\u003e\n**bodyKeysCallback**\n\nThis option can be used to let you choose which fields should be logged on `inbound` `POST`, `PUT` \u0026 `PATCH` requests.\nDefaults to undefined.\nTo use this option, the request's body should be a valid `JSON`.\nMost often this mean that you should register the `Koa` `bodyParser` middleware\n(https://www.npmjs.com/package/body-parser) (or something equivalent),\nbefore registering the `riviere` middleware.\n\nThis option will override `bodyKeys` and `bodyKeysRegex`\n\n*Example*:\n\n```js\n{\n    bodyKeysCallback: (body: any, ctx?: KoaContext) =\u003e {\n        if (!ctx) return body;\n        return {\n            ...body,\n            fields: body.fields.filter(f =\u003e !f._obfuscated)\n        }\n    };\n}\n```\n\n\u003ca name=\"options_color\"\u003e\u003c/a\u003e\n**color**\n\nLog colored log messages. Defaults to: `true`\n\n*Example*:\n```js\n{\n    color: true\n}\n```\n\n\u003ca name=\"options_styles\"\u003e\u003c/a\u003e\n**styles**\n\nThis option is used to format log messages with specific styles. Defaults to: `['simple']`\nIf multiple options are defined one line is exported for every different style.\nAvailable options are:\n- 'simple': Prints method, path, status code and timing followed by ' | ' and a string containing key-value pairs of object properties\n- 'extended': Same as simple but also contains key-value pairs of all properties (fully compatible with LogEntries)\n- 'json': All object properties as a valid JSON object (fully compatible with Google Stackdriver)\n\n*Example*:\n```js\n{\n    styles: ['simple', 'json']\n}\n```\n\n\u003ca name=\"options_context\"\u003e\u003c/a\u003e\n**context**\n\nThe context to be included in every `inbound_request` and `outbound_response`\nlog message. Defaults to empty Object: `{}`.\n\n*Example*:\n\n```js\n{\n    context: (ctx) =\u003e {\n        return {\n            userId: ctx.request.headers['user-id'],\n            accountId: ctx.request.headers['account-id']\n        };\n    }\n}\n```\n\n\u003ca name=\"options_errors\"\u003e\u003c/a\u003e\n**errors**\n\n\u003ca name=\"options_errors_enabled\"\u003e\u003c/a\u003e\n**errors.enabled**\n\nEnable inbound HTTP traffic logs. Defaults to `true`.\n\n\u003ca name=\"options_errors_callback\"\u003e\u003c/a\u003e\n**errors.callback**\n\nControl how the server handles any unhandled errors inside a request's context.\nThe default is to re-throw the unhandled error.\n\n*Example*:\n\n```js\n{\n    errors: {\n        callback: (err, ctx) =\u003e {\n            ctx.status = err.status || 500;\n\n            if (ctx.status \u003c 500) {\n                ctx.body = {error: err.message};\n            } else {\n                ctx.body = {error: 'Internal server error'};\n            }\n        }\n    }\n}\n```\n\n\u003ca name=\"options_headers_regex\"\u003e\u003c/a\u003e\n**headersRegex**\n\nSpecify a regular expression to match the request headers,\nthat should be included in every `inbound_request` log message.\nDefaults to `new RegExp('^X-.+', 'i')`.\nAll the inbound request's headers starting with \"X-\" will be logged by default.\n\n*Example*:\n\n```js\n{\n    headersRegex: new RegExp('X-.+', 'i')\n}\n```\n\n\u003ca name=\"options_header_value_callback\"\u003e\u003c/a\u003e\n**headerValueCallback**\n\nThis option can be used to let you modify or change part or the whole value of a header. \nThis function will be applied to all headers that are passed based on the `headersRegex`. \nThis function takes two argument, the `key` (i.e. the header name) and the `value` (i.e. the header value) and returns the new value of the header.\nThis function is very useful in cases where is mandatory to hide or remove sensitive data that is part of the header value.\nDefaults to undefined. In case of undefined then the header value will remain as is.\n\n*Example*:\nIn this example we hide some sensitive `id_token` value.\n\n```js\n{\n    headerValueCallback: (key, value) {\n        const regex = /id_token=[\\w-]+\\.[\\w-]+\\.[\\w-]+/i;\n        return value.replace(regex, 'id_token=***');\n    };\n}\n```\n\n\u003ca name=\"options_health\"\u003e\u003c/a\u003e\n**health**\n\nSpecify your health endpoints in order to log a minimum subset of information,\nfor these `inbound_requests`. Defaults to `[]`.\nThis may be useful when: You have a load balancer or other system that pings your server at a specific end-point,\nperiodically, to determine the health of your server, and you do not want to log much details regarding these requests.\n\n*Example*\n\n```js\n{\n    health: [\n        {\n            path: '/health',\n            method: 'GET'\n        }\n    ]\n}\n```\n\n\u003ca name=\"options_outbound\"\u003e\u003c/a\u003e\n**outbound**\n\n\u003ca name=\"options_outbound_enabled\"\u003e\u003c/a\u003e\n**outbound.enabled**\n\nEnable outbound HTTP traffic logs. Defaults to `true`.\n\n*Example*:\n\n```js\n{\n    outbound: {\n        enabled: true\n    }\n}\n```\n\n\u003ca name=\"options_outbound_request_enabled\"\u003e\u003c/a\u003e\n**outbound.request.enabled**\n\nEnable outbound_request HTTP traffic logging. Defaults to `true`.\n\n\u003ca name=\"options_outbound_max_body_value_chars\"\u003e\u003c/a\u003e\n**outbound.maxBodyValueChars**\n\nThis option can be used to truncate values `JSON` body of the `outbound` `POST` requests to a specified length. Defaults to undefined.\nTo use this option, the `POST` request's body should be a valid `JSON`.\n\n*Example*:\n\n```js\n{\n    outbound: {\n        maxBodyValueChars: 1024\n    }\n}\n```\n\n\u003ca name=\"options_outbound_blacklisted_path_regex\"\u003e\u003c/a\u003e\n**outbound.blacklistedPathRegex**\n\nThis option can be used to prevent specific outbound paths to be logged.\nDefaults to [].\n\n*Example*:\n\n```js\n{\n    outbound: {\n        blacklistedPathRegex: /\\/health/\n    }\n}\n```\n\n\u003ca name=\"options_outbound_request_obfuscate_href_if_header_exists\"\u003e\u003c/a\u003e\n**outbound.request.obfuscatedHeaders**\n\nThis option can be used to prevent specific URL paths from being logged.\nEvery request that carries a header with a key that matches the values\nwill obfuscate the URL path when logging. \u003cbr\u003e\nDefaults to `'X-Riviere-obfuscate'`\n\n*Example*:\n\n```js\n{\n    outbound: {\n        obfuscateHrefIfHeaderExists: 'X-Riviere-obfuscate'\n    }\n}\n```\n\n\u003ca name=\"options_trace_header_name\"\u003e\u003c/a\u003e\n**traceHeaderName**\n\nTheis is a Header key for the request id header.\nDefaults to: `X-Riviere-Id`.\nIf you already use a request id header you may need to set this options.\nFor example for Heroku deployments,\nyou most often want to set the `riviere` `traceHeaderName` to: `X-Request-Id`\n(https://devcenter.heroku.com/articles/http-request-id)\n\n*Example*:\n\n```js\n{\n    traceHeaderName: 'X-Request-Id'\n}\n```\n\n---\n\n\u003c!---\n**outbound.info**\n\nSet the log level for outbound HTTP traffic. Defaults to `info`.\n\n**sync**\n\nControl whether logs should be flushed in a sync or async fashion (experimental). Defaults to `true`.\n\n*Example*\n\n```js\n{\n  headersRegex: /X-.+/ to match all custom HTTP headers.\n}\n```\n--\u003e\n\n\u003c!---\n**logger**\n\nPass the logger object\n\n*Example of the default logger*\n\n```js\n{\n  info: console.log,\n  error: console.error\n}\n```\n--\u003e\n\u003c!---\n**inbound.info**\n\nSet the log level for inbound HTTP traffic. Defaults to `info`.\n--\u003e\n\n\u003ca name=\"License\"\u003e\u003c/a\u003e\n## License\n\n  MIT\n  (![see LICENCE](https://github.com/Workable/riviere/blob/master/LICENCE))\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fworkable%2Friviere","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fworkable%2Friviere","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fworkable%2Friviere/lists"}