{"_id":"@kilohealth/web-app-monitoring","_rev":"45-6bdfaba288116d5c325bd4c60d98c16f","name":"@kilohealth/web-app-monitoring","dist-tags":{"latest":"2.1.2","alpha":"2.1.1-alpha.2","beta":"1.0.0-beta.1"},"versions":{"1.0.0":{"name":"@kilohealth/web-app-monitoring","version":"1.0.0","license":"MIT","author":{"name":"Kilo Health"},"main":"dist/index.js","scripts":{"build":"rimraf dist && tsc","prepare":"husky install","test":"jest"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","dd-trace":"^4.0.0","pino":"^8.14.1"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"gitHead":"d9bb5d8329c20f4f3c1d1f4387bbdae07b74ad06","description":"[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)","_id":"@kilohealth/web-app-monitoring@1.0.0","_nodeVersion":"18.16.0","_npmVersion":"9.5.1","dist":{"integrity":"sha512-Z2bA3G7ER7zJH27BnXl1fNYUrzQqJX07lSUlw2jZD7s+xB0I5OmTsRaWhLtE8zLYbsX8tgOhiva6HvnH+BVrVg==","shasum":"21cd23603bb61d057d3d29cf926202b7525ffe7c","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.0.0.tgz","fileCount":20,"unpackedSize":19448,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIF5JJriwdVETnGB/+lYHo9/4+RSaEHxhvhjGXLNy2EU3AiEAgRYZ2NwUVA7v2cpadLUOlhBiwQMWzSccTmrMkFuJbJk="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.0.0_1684811875527_0.8517652271558172"},"_hasShrinkwrap":false},"1.0.0-alpha.1":{"name":"@kilohealth/web-app-monitoring","version":"1.0.0-alpha.1","license":"MIT","author":{"name":"Kilo Health"},"main":"dist/index.js","scripts":{"build":"rimraf dist && tsc","prepare":"husky install","semantic-release":"semantic-release","test":"jest"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","dd-trace":"^4.0.0","pino":"^8.14.1"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"readme":"# @frontend/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\nThis package is for monitoring and error tracking. It can be initialized with DataDog now and usual console, depending on provided options.\n\n## Table of Contents\n\n1. [Getting started](#getting-started)\n2. [Usage in project](#usage-in-project)\n3. [Methods](#methods)\n\n## Getting started\n\nAdd dependency\n\n```\n$ npm install @frontend/web-app-monitoring\n```\n\n## Usage in project\n\n```js\nimport { BrowserMonitoringService } from '@frontend/web-app-monitoring';\n\nexport const localMonitoring = new BrowserMonitoringService();\nlocalMonitoring.info('Smth happened');\n```\n\nor\n\n```js\nimport { BrowserMonitoringService } from '@frontend/web-app-monitoring';\n\nconst browserMonitoringService = new BrowserMonitoringService({\n  serviceName: 'serviceName',\n  serviceEnv: 'serviceEnv',\n  serviceVersion: 'serviceVersion',\n  clientToken: 'clientToken',\n});\nbrowserMonitoringService.info('Smth happened');\n```\n\n## Methods\n\n- `info`: adds item to logs (either local or remote, depending on setup);\n- `reportError`: reports error to monitoring system (either local or remote, depending on setup);\n- `setupReportingNativeLogs`: intersects native logs and report them into remote system (in case needed params for remote system provided);\n","readmeFilename":"README.md","gitHead":"c00a53b49b2e96e3cbdd11645a5559aac9d3b787","description":"[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)","_id":"@kilohealth/web-app-monitoring@1.0.0-alpha.1","_nodeVersion":"18.16.0","_npmVersion":"9.6.7","dist":{"integrity":"sha512-3uihjpaUMULnOok90FRx/tB7OY59CgkiG1e7J4o0o4fX/lUZP2LjtbhmftZidFh9+d7m2XwZM3EJegmtNg32yA==","shasum":"83e2f6f240289d89eb5dc933aead18563fdf83ad","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.0.0-alpha.1.tgz","fileCount":20,"unpackedSize":19813,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICSOAHhp+HS//p49tttq+/TNZhMze9jp3TKHIYWwryBIAiBBEOwDzGgPPCiV7cE4Smqx9SeStYMUnFWijTMEDSO4+A=="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.0.0-alpha.1_1684813210238_0.38684196520818515"},"_hasShrinkwrap":false},"1.0.0-alpha.2":{"name":"@kilohealth/web-app-monitoring","version":"1.0.0-alpha.2","license":"MIT","author":{"name":"Kilo Health"},"main":"dist/index.js","scripts":{"build":"rimraf dist && tsc","prepare":"husky install","semantic-release":"semantic-release","test":"jest"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","dd-trace":"^4.0.0","pino":"^8.14.1"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"readme":"# @frontend/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\nThis package is for monitoring and error tracking. It can be initialized with DataDog now and usual console, depending on provided options.\n\n## Table of Contents\n\n1. [Getting started](#getting-started)\n2. [Usage in project](#usage-in-project)\n3. [Methods](#methods)\n\n## Getting started\n\nAdd dependency\n\n```\n$ npm install @frontend/web-app-monitoring\n```\n\n## Usage in project\n\n```js\nimport { BrowserMonitoringService } from '@frontend/web-app-monitoring';\n\nexport const localMonitoring = new BrowserMonitoringService();\nlocalMonitoring.info('Smth happened');\n```\n\nor\n\n```js\nimport { BrowserMonitoringService } from '@frontend/web-app-monitoring';\n\nconst browserMonitoringService = new BrowserMonitoringService({\n  serviceName: 'serviceName',\n  serviceEnv: 'serviceEnv',\n  serviceVersion: 'serviceVersion',\n  clientToken: 'clientToken',\n});\nbrowserMonitoringService.info('Smth happened');\n```\n\n## Methods\n\n- `debug, info, warn, error`: adds item to logs (either local or remote, depending on setup);\n- `reportError`: reports error to monitoring system (either local or remote, depending on setup);\n","readmeFilename":"README.md","gitHead":"a261c838e72e5fe274ea72eca26428182ae06b3f","description":"[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)","_id":"@kilohealth/web-app-monitoring@1.0.0-alpha.2","_nodeVersion":"18.16.0","_npmVersion":"9.6.7","dist":{"integrity":"sha512-VcsJ4WexPthZj6oF2CpWYx6kgAG3HLBTzL9q0WCGXP2eIqrDUuPuf8r5lArKUV95oEmcyS7isW0ArXBg08K1qA==","shasum":"4fa2467afa2cafe3ef053e59c407571fb3abaae7","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.0.0-alpha.2.tgz","fileCount":20,"unpackedSize":20199,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFMA/YB10qymrt08CJc7T5ZBLslO+ex8xORxNPXqVLigAiEAxuWrDG5MrWCTgFsELiVW6XxOQ8Ov/K8VwfRGxEqtjqk="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.0.0-alpha.2_1684815970024_0.4750351752389472"},"_hasShrinkwrap":false},"1.0.0-alpha.3":{"name":"@kilohealth/web-app-monitoring","version":"1.0.0-alpha.3","license":"MIT","author":{"name":"Kilo Health"},"main":"dist/index.js","scripts":{"build":"rimraf dist && tsc","prepare":"husky install","semantic-release":"semantic-release","test":"jest"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","dd-trace":"^4.0.0","pino":"^8.14.1"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"readme":"# @frontend/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\nThis package is for monitoring and error tracking. It can be initialized with DataDog now and usual console, depending on provided options.\n\n## Table of Contents\n\n1. [Getting started](#getting-started)\n2. [Usage in project](#usage-in-project)\n3. [Methods](#methods)\n\n## Getting started\n\nAdd dependency\n\n```\n$ npm install @frontend/web-app-monitoring\n```\n\n## Usage in project\n\n```js\nimport { BrowserMonitoringService } from '@frontend/web-app-monitoring';\n\nexport const localMonitoring = new BrowserMonitoringService();\nlocalMonitoring.info('Smth happened');\n```\n\nor\n\n```js\nimport { BrowserMonitoringService } from '@frontend/web-app-monitoring';\n\nconst browserMonitoringService = new BrowserMonitoringService({\n  serviceName: 'serviceName',\n  serviceEnv: 'serviceEnv',\n  serviceVersion: 'serviceVersion',\n  clientToken: 'clientToken',\n});\nbrowserMonitoringService.info('Smth happened');\n```\n\n## Methods\n\n- `debug, info, warn, error`: adds item to logs (either local or remote, depending on setup);\n- `reportError`: reports error to monitoring system (either local or remote, depending on setup);\n","readmeFilename":"README.md","gitHead":"7536859c1a5dad7647891cce2244e9f62aa10800","description":"[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)","_id":"@kilohealth/web-app-monitoring@1.0.0-alpha.3","_nodeVersion":"18.16.0","_npmVersion":"9.6.7","dist":{"integrity":"sha512-es8pkJEwu1cw/4nAQY0kXpGB7kBErc2r6WPVl22VuG1tV3MR26FPM9/XlOjDHIAgHDnkbTeSkKnwXYWvjN3Cgw==","shasum":"705b81fc318351a6deece703a318b1c08778ef3b","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.0.0-alpha.3.tgz","fileCount":27,"unpackedSize":29813,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICKLYKwkpJ0c7/OHPkZdLwWiecUH6BcwrMbuPJLiseBkAiBngpvddsDt2qybbjyWaWvgnIlQa7H8Fbh54hBDlnzqeA=="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.0.0-alpha.3_1684908185767_0.6343536078483345"},"_hasShrinkwrap":false},"1.0.0-alpha.4":{"name":"@kilohealth/web-app-monitoring","version":"1.0.0-alpha.4","license":"MIT","author":{"name":"Kilo Health"},"main":"dist/index.js","scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli ","prepare":"husky install","semantic-release":"semantic-release","test":"jest","upload:sourcemaps":"./dist/cli/sourcemap-uploader.sh"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","@datadog/datadog-ci":"^2.11.0","dd-trace":"^4.0.0","pino":"^8.14.1"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"readme":"# @frontend/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\nThis package is for monitoring and error tracking. It can be initialized with DataDog now and usual console, depending on provided options.\n\n## Table of Contents\n\n1. [Getting started](#getting-started)\n2. [Usage in project](#usage-in-project)\n3. [Methods](#methods)\n\n## Getting started\n\nAdd dependency\n\n```\n$ npm install @frontend/web-app-monitoring\n```\n\n## Usage in project\n\n```js\nimport { BrowserMonitoringService } from '@frontend/web-app-monitoring';\n\nexport const localMonitoring = new BrowserMonitoringService();\nlocalMonitoring.info('Smth happened');\n```\n\nor\n\n```js\nimport { BrowserMonitoringService } from '@frontend/web-app-monitoring';\n\nconst browserMonitoringService = new BrowserMonitoringService({\n  serviceName: 'serviceName',\n  serviceEnv: 'serviceEnv',\n  serviceVersion: 'serviceVersion',\n  clientToken: 'clientToken',\n});\nbrowserMonitoringService.info('Smth happened');\n```\n\n## Methods\n\n- `debug, info, warn, error`: adds item to logs (either local or remote, depending on setup);\n- `reportError`: reports error to monitoring system (either local or remote, depending on setup);\n","readmeFilename":"README.md","gitHead":"7c06b1aaeb03300fce7759644633a7f1977dfc5a","description":"[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)","_id":"@kilohealth/web-app-monitoring@1.0.0-alpha.4","_nodeVersion":"18.16.0","_npmVersion":"9.6.7","dist":{"integrity":"sha512-DJ6cQ9b/mlQYFkz0Jg+flhqu8cpyLX7pUmAoJSyyYYDHFfHJc8PLopsgaQX7ymBDkPbZNaES8lCK+vS/L047JA==","shasum":"da50ec8a9e68de99d158d46019d0590c78fa46e9","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.0.0-alpha.4.tgz","fileCount":29,"unpackedSize":32627,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEVKH9qa60jDIGrOGPOmmQTkC5lQmswjmkpiba8zsiR2AiBb+iUqkJliPGG931dYq5RKtGfTbDnNW9n7PDQCAjKsnQ=="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.0.0-alpha.4_1684910140520_0.12469501341806555"},"_hasShrinkwrap":false},"1.0.0-alpha.5":{"name":"@kilohealth/web-app-monitoring","version":"1.0.0-alpha.5","license":"MIT","author":{"name":"Kilo Health"},"main":"dist/index.js","bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","prepare":"husky install","semantic-release":"semantic-release","test":"jest","upload:sourcemaps":"./dist/cli/sourcemap-uploader.sh"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","@datadog/datadog-ci":"^2.11.0","dd-trace":"^4.0.0","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"types":"./dist/index.d.ts","readme":"# @frontend/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\nThis package is for monitoring and error tracking.\nIt consists of 3 parts - browser, server and cli.\n\n## Table of Contents\n\n1. [Getting started](#getting-started)\n2. [Usage in project](#usage-in-project)\n3. [Methods](#methods)\n\n## Getting started\n\nAdd dependency\n\n```\n$ npm install @frontend/web-app-monitoring\n```\n\n## Usage in project\n\n### Browser or Server Methods\n\n- `debug, info, warn, error`: adds item to logs (either local or remote, depending on setup);\n- `reportError`: reports error to monitoring system (either local or remote, depending on setup);\n\n```js\nimport { BrowserMonitoringService } from '@frontend/web-app-monitoring';\n\nexport const localMonitoring = new BrowserMonitoringService();\nlocalMonitoring.info('Smth happened');\n```\n\nor\n\n```js\nimport { ServerMonitoringService } from '@frontend/web-app-monitoring';\n\nconst serverMonitoringService = new ServerMonitoringService({\n  serviceName: 'serviceName',\n  serviceEnv: 'serviceEnv',\n  serviceVersion: 'serviceVersion',\n  clientToken: 'clientToken',\n});\nserverMonitoringService.info('Smth happened');\n```\n\n### Server Only\n\n- `initServerMonitoring`: creates serverMonitoringService under the hood and can do additional utility functions.\n  Returns instance of serverMonitoringService.\n\n```js\nimport { initServerMonitoring } from '@frontend/web-app-monitoring';\n\nconst remoteMonitoringServiceParams = {\n  serviceName: `${process.env.MONITORING_TOOL__SERVICE_NAME}__next-server`,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n};\ninitServerMonitoring(remoteMonitoringServiceParams, {\n  shouldOverrideNativeConsole: true,\n  shouldCatchProcessErrors: true,\n  globalMonitoringInstanceName: 'kiloServerMonitoring',\n});\n```\n\n#### Params:\n\n- `shouldOverrideNativeConsole` - defaults to false. When true - service will override native console with itself.\n- `shouldCatchProcessErrors` - defaults to false. When true - service will subscribe to native errors and report them\n- `globalMonitoringInstanceName` - defaults to empty string. When provided - service will put itself into global node scope under provided name. So it can be accessed in other parts of the code.\n\n- `initTracing`: initiate tracing of application. Need to be provided with remote monitoring system params.\n  Returns instance of tracer.\n\n```js\nimport { initTracing } from '@frontend/web-app-monitoring';\n\nconst remoteMonitoringServiceParams = {\n  serviceName: `${process.env.MONITORING_TOOL__SERVICE_NAME}__next-server`,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n};\ninitTracing(remoteMonitoringServiceParams);\n```\n\n### CLI usage\n\nIn order to upload sourcemaps to remote monitoring service you can use cli from this package like `web-app-monitoring__upload-sourcemaps` as shown below\n\n```json\n{\n  \"scripts\": {\n    \"upload:sourcemaps\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 npm run build && MONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\"\n  }\n}\n```\n\n- `MONITORING_TOOL__BUILD_DIR` - relative path to bulid folder, where sourcemaps are located\n- `MONITORING_TOOL__PUBLIC_PATH` - relative public path to js files, when code is served in production\n","readmeFilename":"README.md","gitHead":"6397356b30d1ff529b1dd1390c961a4257b07e08","description":"[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)","_id":"@kilohealth/web-app-monitoring@1.0.0-alpha.5","_nodeVersion":"18.16.0","_npmVersion":"9.6.7","dist":{"integrity":"sha512-OxYKsK3GFZvjTox5bYjmvW1I5A5IECRe/SHLgaKteuk+hHiVI0J5hVu/Koc9Ddd8+7lZPsXWOGxRocZnj3SbeQ==","shasum":"080a375d3091411118fa948c30ddf49b7712bf9d","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.0.0-alpha.5.tgz","fileCount":57,"unpackedSize":47367,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDxLP1QVkMxKuM0YhjjAqb5GKCdmegIDAyC4bvsqjPzLgIhAK2dkgn2qeOPUBK6GDbjmw2R4rQvbaU8qX53JVc7dCb4"}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.0.0-alpha.5_1685341157736_0.43460746668580574"},"_hasShrinkwrap":false},"1.0.0-alpha.6":{"name":"@kilohealth/web-app-monitoring","version":"1.0.0-alpha.6","license":"MIT","author":{"name":"Kilo Health"},"main":"dist/index.js","bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","prepare":"husky install","semantic-release":"semantic-release","test":"jest","upload:sourcemaps":"./dist/cli/sourcemap-uploader.sh"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","@datadog/datadog-ci":"^2.11.0","dd-trace":"^4.0.0","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"types":"./dist/index.d.ts","readme":"# @frontend/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\nThis package is for monitoring and error tracking.\nIt consists of 3 parts - browser, server and cli.\n\n## Table of Contents\n\n1. [Getting started](#getting-started)\n2. [Usage in project](#usage-in-project)\n3. [Methods](#methods)\n\n## Getting started\n\nAdd dependency\n\n```\n$ npm install @frontend/web-app-monitoring\n```\n\n## Usage in project\n\n### Browser or Server Methods\n\n- `debug, info, warn, error`: adds item to logs (either local or remote, depending on setup);\n- `reportError`: reports error to monitoring system (either local or remote, depending on setup);\n\n```js\nimport { BrowserMonitoringService } from '@frontend/web-app-monitoring';\n\nexport const localMonitoring = new BrowserMonitoringService();\nlocalMonitoring.info('Smth happened');\n```\n\nor\n\n```js\nimport { ServerMonitoringService } from '@frontend/web-app-monitoring';\n\nconst serverMonitoringService = new ServerMonitoringService({\n  serviceName: 'serviceName',\n  serviceEnv: 'serviceEnv',\n  serviceVersion: 'serviceVersion',\n  clientToken: 'clientToken',\n});\nserverMonitoringService.info('Smth happened');\n```\n\n### Server Only\n\n- `initServerMonitoring`: creates serverMonitoringService under the hood and can do additional utility functions.\n  Returns instance of serverMonitoringService.\n\n```js\nimport { initServerMonitoring } from '@frontend/web-app-monitoring';\n\nconst remoteMonitoringServiceParams = {\n  serviceName: `${process.env.MONITORING_TOOL__SERVICE_NAME}__next-server`,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n};\ninitServerMonitoring(remoteMonitoringServiceParams, {\n  shouldOverrideNativeConsole: true,\n  shouldCatchProcessErrors: true,\n  globalMonitoringInstanceName: 'kiloServerMonitoring',\n});\n```\n\n#### Params:\n\n- `shouldOverrideNativeConsole` - defaults to false. When true - service will override native console with itself.\n- `shouldCatchProcessErrors` - defaults to false. When true - service will subscribe to native errors and report them\n- `globalMonitoringInstanceName` - defaults to empty string. When provided - service will put itself into global node scope under provided name. So it can be accessed in other parts of the code.\n\n- `initTracing`: initiate tracing of application. Need to be provided with remote monitoring system params.\n  Returns instance of tracer.\n\n```js\nimport { initTracing } from '@frontend/web-app-monitoring';\n\nconst remoteMonitoringServiceParams = {\n  serviceName: `${process.env.MONITORING_TOOL__SERVICE_NAME}__next-server`,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n};\ninitTracing(remoteMonitoringServiceParams);\n```\n\n### CLI usage\n\nIn order to upload sourcemaps to remote monitoring service you can use cli from this package like `web-app-monitoring__upload-sourcemaps` as shown below\n\n```json\n{\n  \"scripts\": {\n    \"upload:sourcemaps\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 npm run build && MONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\"\n  }\n}\n```\n\n- `MONITORING_TOOL__BUILD_DIR` - relative path to bulid folder, where sourcemaps are located\n- `MONITORING_TOOL__PUBLIC_PATH` - relative public path to js files, when code is served in production\n","readmeFilename":"README.md","gitHead":"0269aaa944045db286bab0a407e648313c549be6","description":"[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)","_id":"@kilohealth/web-app-monitoring@1.0.0-alpha.6","_nodeVersion":"18.16.0","_npmVersion":"9.6.7","dist":{"integrity":"sha512-MOStOti6rgxcF31+NeXxv1SMy+q1ki3LXKuT0Nq1COiQqnKcrzkjgBj9quTmZ55WoVnmWCcFptkpNNkDEB2oJA==","shasum":"d6194869dfdfbf1cc8207e623a74c3576dcbc3ad","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.0.0-alpha.6.tgz","fileCount":57,"unpackedSize":48157,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCbU1EUkH8onAvkFEhL08+tPXwW6k5LHemtqb+dZnzUJwIhAI3TmR8XDQznhDsksAEcCzlNFZk2JTfeCXIpDPTpBgbi"}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.0.0-alpha.6_1685341319255_0.6028967183280272"},"_hasShrinkwrap":false},"1.0.0-alpha.7":{"name":"@kilohealth/web-app-monitoring","version":"1.0.0-alpha.7","license":"MIT","author":{"name":"Kilo Health"},"main":"dist/index.js","bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","prepare":"husky install","semantic-release":"semantic-release","test":"jest","upload:sourcemaps":"./dist/cli/sourcemap-uploader.sh"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","@datadog/datadog-ci":"^2.11.0","dd-trace":"^4.0.0","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"readme":"# @frontend/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\nThis package is for monitoring and error tracking.\nIt consists of 3 parts - browser, server and cli.\n\n## Table of Contents\n\n1. [Getting started](#getting-started)\n2. [Usage in project](#usage-in-project)\n3. [Methods](#methods)\n\n## Getting started\n\nAdd dependency\n\n```\n$ npm install @frontend/web-app-monitoring\n```\n\n## Usage in project\n\n### Browser or Server Methods\n\n- `debug, info, warn, error`: adds item to logs (either local or remote, depending on setup);\n- `reportError`: reports error to monitoring system (either local or remote, depending on setup);\n\n```js\nimport { BrowserMonitoringService } from '@frontend/web-app-monitoring';\n\nexport const localMonitoring = new BrowserMonitoringService();\nlocalMonitoring.info('Smth happened');\n```\n\nor\n\n```js\nimport { ServerMonitoringService } from '@frontend/web-app-monitoring';\n\nconst serverMonitoringService = new ServerMonitoringService({\n  serviceName: 'serviceName',\n  serviceEnv: 'serviceEnv',\n  serviceVersion: 'serviceVersion',\n  clientToken: 'clientToken',\n});\nserverMonitoringService.info('Smth happened');\n```\n\n### Server Only\n\n- `initServerMonitoring`: creates serverMonitoringService under the hood and can do additional utility functions.\n  Returns instance of serverMonitoringService.\n\n```js\nimport { initServerMonitoring } from '@frontend/web-app-monitoring';\n\nconst remoteMonitoringServiceParams = {\n  serviceName: `${process.env.MONITORING_TOOL__SERVICE_NAME}__next-server`,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n};\ninitServerMonitoring(remoteMonitoringServiceParams, {\n  shouldOverrideNativeConsole: true,\n  shouldCatchProcessErrors: true,\n  globalMonitoringInstanceName: 'kiloServerMonitoring',\n});\n```\n\n#### Params:\n\n- `shouldOverrideNativeConsole` - defaults to false. When true - service will override native console with itself.\n- `shouldCatchProcessErrors` - defaults to false. When true - service will subscribe to native errors and report them\n- `globalMonitoringInstanceName` - defaults to empty string. When provided - service will put itself into global node scope under provided name. So it can be accessed in other parts of the code.\n\n- `initTracing`: initiate tracing of application. Need to be provided with remote monitoring system params.\n  Returns instance of tracer.\n\n```js\nimport { initTracing } from '@frontend/web-app-monitoring';\n\nconst remoteMonitoringServiceParams = {\n  serviceName: `${process.env.MONITORING_TOOL__SERVICE_NAME}__next-server`,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n};\ninitTracing(remoteMonitoringServiceParams);\n```\n\n### CLI usage\n\nIn order to upload sourcemaps to remote monitoring service you can use cli from this package like `web-app-monitoring__upload-sourcemaps` as shown below\n\n```json\n{\n  \"scripts\": {\n    \"upload:sourcemaps\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 npm run build && MONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\"\n  }\n}\n```\n\n- `MONITORING_TOOL__BUILD_DIR` - relative path to bulid folder, where sourcemaps are located\n- `MONITORING_TOOL__PUBLIC_PATH` - relative public path to js files, when code is served in production\n","readmeFilename":"README.md","gitHead":"3e96b4df380dfc8bc59739df0cc7ffe4c2c93477","description":"[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)","_id":"@kilohealth/web-app-monitoring@1.0.0-alpha.7","_nodeVersion":"18.16.0","_npmVersion":"9.6.7","dist":{"integrity":"sha512-YfuLU+tQQjvNZ9dX9M+WWyRPbAV2GaBklu+Pm7FxQSeBEBeDM3cPM0Wepb55P8vzvBDbb0fn1KBLDHfh0Dk+Aw==","shasum":"152585bc8c466350a7516f2ebec39c7314df25e1","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.0.0-alpha.7.tgz","fileCount":53,"unpackedSize":47923,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC3PZAcXN28BSOoBCHy3cbTz6m7HqRiv1X147gNrdyvkgIgSF+q0k/ebgvX6e8hJDp6/nDAdfNRczGAtVd65RvGcwI="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.0.0-alpha.7_1685344971725_0.3431405334770459"},"_hasShrinkwrap":false},"1.0.0-alpha.8":{"name":"@kilohealth/web-app-monitoring","version":"1.0.0-alpha.8","license":"MIT","author":{"name":"Kilo Health"},"main":"dist/index.js","bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","prepare":"husky install","semantic-release":"semantic-release","test":"jest","upload:sourcemaps":"./dist/cli/sourcemap-uploader.sh"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","@datadog/datadog-ci":"^2.11.0","dd-trace":"^4.0.0","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jest-environment-jsdom":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"readme":"# @frontend/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\nThis package is for monitoring and error tracking.\nIt consists of 3 parts - browser, server and cli.\n\n## Table of Contents\n\n1. [Getting started](#getting-started)\n2. [Usage in project](#usage-in-project)\n3. [Methods](#methods)\n\n## Getting started\n\nAdd dependency\n\n```\n$ npm install @frontend/web-app-monitoring\n```\n\n## Usage in project\n\n### Browser or Server Methods\n\n- `debug, info, warn, error`: adds item to logs (either local or remote, depending on setup);\n- `reportError`: reports error to monitoring system (either local or remote, depending on setup);\n\n```js\nimport { BrowserMonitoringService } from '@frontend/web-app-monitoring';\n\nexport const localMonitoring = new BrowserMonitoringService();\nlocalMonitoring.info('Smth happened');\n```\n\nor\n\n```js\nimport { ServerMonitoringService } from '@frontend/web-app-monitoring';\n\nconst serverMonitoringService = new ServerMonitoringService({\n  serviceName: 'serviceName',\n  serviceEnv: 'serviceEnv',\n  serviceVersion: 'serviceVersion',\n  clientToken: 'clientToken',\n});\nserverMonitoringService.info('Smth happened');\n```\n\n### Server Only\n\n- `initServerMonitoring`: creates serverMonitoringService under the hood and can do additional utility functions.\n  Returns instance of serverMonitoringService.\n\n```js\nimport { initServerMonitoring } from '@frontend/web-app-monitoring';\n\nconst remoteMonitoringServiceParams = {\n  serviceName: `${process.env.MONITORING_TOOL__SERVICE_NAME}__next-server`,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n};\ninitServerMonitoring(remoteMonitoringServiceParams, {\n  shouldOverrideNativeConsole: true,\n  shouldCatchProcessErrors: true,\n  globalMonitoringInstanceName: 'kiloServerMonitoring',\n});\n```\n\n#### Params:\n\n- `shouldOverrideNativeConsole` - defaults to false. When true - service will override native console with itself.\n- `shouldCatchProcessErrors` - defaults to false. When true - service will subscribe to native errors and report them\n- `globalMonitoringInstanceName` - defaults to empty string. When provided - service will put itself into global node scope under provided name. So it can be accessed in other parts of the code.\n\n- `initTracing`: initiate tracing of application. Need to be provided with remote monitoring system params.\n  Returns instance of tracer.\n\n```js\nimport { initTracing } from '@frontend/web-app-monitoring';\n\nconst remoteMonitoringServiceParams = {\n  serviceName: `${process.env.MONITORING_TOOL__SERVICE_NAME}__next-server`,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n};\ninitTracing(remoteMonitoringServiceParams);\n```\n\n### CLI usage\n\nIn order to upload sourcemaps to remote monitoring service you can use cli from this package like `web-app-monitoring__upload-sourcemaps` as shown below\n\n```json\n{\n  \"scripts\": {\n    \"upload:sourcemaps\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 npm run build && MONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\"\n  }\n}\n```\n\n- `MONITORING_TOOL__BUILD_DIR` - relative path to bulid folder, where sourcemaps are located\n- `MONITORING_TOOL__PUBLIC_PATH` - relative public path to js files, when code is served in production\n\n## Roadmap\n\n- write guide on using instrumentation hook for nextjs server integration\n- write guide on env variables renaming\n- add guide on next integration (TS, global variables, phase in next.config.js)\n","readmeFilename":"README.md","gitHead":"e7cf9d713d60b48b2cb8a6d8fcdcc3cd1153e10e","description":"[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)","_id":"@kilohealth/web-app-monitoring@1.0.0-alpha.8","_nodeVersion":"18.16.0","_npmVersion":"9.6.7","dist":{"integrity":"sha512-x+6yJUuZBRV1q5N8g7szNV2DZVaZc589gXk6M4bp28wE1mVY0OSSD0N7Y6geF7gaT6bEQjIm7MN8qTwV//50rw==","shasum":"3f32ff88569a3b1c3bc57c7c20d5283d3743b251","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.0.0-alpha.8.tgz","fileCount":54,"unpackedSize":53470,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAPoA2gwG0Ly1HgNqow1GlsCqaqZ7JbRZssfKawOboqPAiA5nQ5j2Hz0NFcyMfSxssZhfMoNGbuUww559ulI1neoFQ=="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.0.0-alpha.8_1685430234103_0.08508653352962936"},"_hasShrinkwrap":false},"1.0.0-alpha.9":{"name":"@kilohealth/web-app-monitoring","version":"1.0.0-alpha.9","license":"MIT","author":{"name":"Kilo Health"},"main":"dist/index.js","bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","prepare":"husky install","semantic-release":"semantic-release","test":"jest","upload:sourcemaps":"./dist/cli/sourcemap-uploader.sh"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","@datadog/datadog-ci":"^2.11.0","dd-trace":"^4.0.0","lodash":"^4.17.21","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","@types/lodash":"^4.14.195","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jest-environment-jsdom":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"readme":"# @frontend/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\nThis package is for monitoring and error tracking.\nIt consists of 3 parts - browser, server and cli.\n\n## Table of Contents\n\n1. [Getting started](#getting-started)\n2. [Usage in project](#usage-in-project)\n3. [Methods](#methods)\n\n## Getting started\n\nAdd dependency\n\n```\n$ npm install @frontend/web-app-monitoring\n```\n\n## Usage in project\n\n### Browser or Server Methods\n\n- `debug, info, warn, error`: adds item to logs (either local or remote, depending on setup);\n- `reportError`: reports error to monitoring system (either local or remote, depending on setup);\n\n```js\nimport { BrowserMonitoringService } from '@frontend/web-app-monitoring';\n\nexport const localMonitoring = new BrowserMonitoringService();\nlocalMonitoring.info('Smth happened');\n```\n\nor\n\n```js\nimport { ServerMonitoringService } from '@frontend/web-app-monitoring';\n\nconst serverMonitoringService = new ServerMonitoringService({\n  serviceName: 'serviceName',\n  serviceEnv: 'serviceEnv',\n  serviceVersion: 'serviceVersion',\n  clientToken: 'clientToken',\n});\nserverMonitoringService.info('Smth happened');\n```\n\n### Server Only\n\n- `initServerMonitoring`: creates serverMonitoringService under the hood and can do additional utility functions.\n  Returns instance of serverMonitoringService.\n\n```js\nimport { initServerMonitoring } from '@frontend/web-app-monitoring';\n\nconst remoteMonitoringServiceParams = {\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n};\ninitServerMonitoring(remoteMonitoringServiceParams, {\n  shouldOverrideNativeConsole: true,\n  shouldCatchProcessErrors: true,\n  globalMonitoringInstanceName: 'kiloServerMonitoring',\n});\n```\n\n#### Params:\n\n- `shouldOverrideNativeConsole` - defaults to false. When true - service will override native console with itself.\n- `shouldCatchProcessErrors` - defaults to false. When true - service will subscribe to native errors and report them\n- `globalMonitoringInstanceName` - defaults to empty string. When provided - service will put itself into global node scope under provided name. So it can be accessed in other parts of the code.\n\n- `initTracing`: initiate tracing of application. Need to be provided with remote monitoring system params.\n  Returns instance of tracer.\n\n```js\nimport { initTracing } from '@frontend/web-app-monitoring';\n\nconst remoteMonitoringServiceParams = {\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n};\ninitTracing(remoteMonitoringServiceParams);\n```\n\n### CLI usage\n\nIn order to upload sourcemaps to remote monitoring service you can use cli from this package like `web-app-monitoring__upload-sourcemaps` as shown below\n\n```json\n{\n  \"scripts\": {\n    \"upload:sourcemaps\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 npm run build && MONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\"\n  }\n}\n```\n\n- `MONITORING_TOOL__BUILD_DIR` - relative path to bulid folder, where sourcemaps are located\n- `MONITORING_TOOL__PUBLIC_PATH` - relative public path to js files, when code is served in production\n\n## Roadmap\n\n- write guide on using instrumentation hook for nextjs server integration\n- write guide on env variables renaming\n- add guide on next integration (TS, global variables, phase in next.config.js)\n","readmeFilename":"README.md","gitHead":"61604a4ff9c70832e0cb0e966f3a8adb7c4b9db2","description":"[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)","_id":"@kilohealth/web-app-monitoring@1.0.0-alpha.9","_nodeVersion":"18.16.0","_npmVersion":"9.6.7","dist":{"integrity":"sha512-Sa+1ZQSMxY9Ke9+KVveRZeLaOrJ9TOFaBERh0XNNmwa/LkuV9Ei33HHO/x2/iTBSrfw27Qy3S0wz6oOSMXN2Ig==","shasum":"b38d361a5953089545a391120703c51fe0e059aa","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.0.0-alpha.9.tgz","fileCount":56,"unpackedSize":65729,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIE71U+M3VtheL0w2KH786zseErZH4SpctDeFWYb0qv5JAiB3pDJwtCh20JN3Tl03lobXtYhu/ROSj/uIxNZTYcKpzQ=="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.0.0-alpha.9_1685440202908_0.9853658110419847"},"_hasShrinkwrap":false},"1.0.0-alpha.10":{"name":"@kilohealth/web-app-monitoring","version":"1.0.0-alpha.10","license":"MIT","author":{"name":"Kilo Health"},"main":"dist/index.js","bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","prepare":"husky install","semantic-release":"semantic-release","test":"jest","upload:sourcemaps":"./dist/cli/sourcemap-uploader.sh"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","@datadog/datadog-ci":"^2.11.0","dd-trace":"^4.0.0","lodash":"^4.17.21","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","@types/lodash":"^4.14.195","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jest-environment-jsdom":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"readme":"# @frontend/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\nThis package is for monitoring and error tracking.\nIt consists of 3 parts - browser, server and cli.\n\n## Table of Contents\n\n1. [Getting started](#getting-started)\n2. [Usage in project](#usage-in-project)\n3. [Methods](#methods)\n\n## Getting started\n\nAdd dependency\n\n```\n$ npm install @frontend/web-app-monitoring\n```\n\n## Usage in project\n\n### Browser or Server Methods\n\n- `debug, info, warn, error`: adds item to logs (either local or remote, depending on setup);\n- `reportError`: reports error to monitoring system (either local or remote, depending on setup);\n\n```js\nimport { BrowserMonitoringService } from '@frontend/web-app-monitoring';\n\nexport const localMonitoring = new BrowserMonitoringService();\nlocalMonitoring.info('Smth happened');\n```\n\nor\n\n```js\nimport { ServerMonitoringService } from '@frontend/web-app-monitoring';\n\nconst serverMonitoringService = new ServerMonitoringService({\n  serviceName: 'serviceName',\n  serviceEnv: 'serviceEnv',\n  serviceVersion: 'serviceVersion',\n  clientToken: 'clientToken',\n});\nserverMonitoringService.info('Smth happened');\n```\n\n### Server Only\n\n- `initServerMonitoring`: creates serverMonitoringService under the hood and can do additional utility functions.\n  Returns instance of serverMonitoringService.\n\n```js\nimport { initServerMonitoring } from '@frontend/web-app-monitoring';\n\nconst remoteMonitoringServiceParams = {\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n};\ninitServerMonitoring(remoteMonitoringServiceParams, {\n  shouldOverrideNativeConsole: true,\n  shouldCatchProcessErrors: true,\n  globalMonitoringInstanceName: 'kiloServerMonitoring',\n});\n```\n\n#### Params:\n\n- `shouldOverrideNativeConsole` - defaults to false. When true - service will override native console with itself.\n- `shouldCatchProcessErrors` - defaults to false. When true - service will subscribe to native errors and report them\n- `globalMonitoringInstanceName` - defaults to empty string. When provided - service will put itself into global node scope under provided name. So it can be accessed in other parts of the code.\n\n- `initTracing`: initiate tracing of application. Need to be provided with remote monitoring system params.\n  Returns instance of tracer.\n\n```js\nimport { initTracing } from '@frontend/web-app-monitoring';\n\nconst remoteMonitoringServiceParams = {\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n};\ninitTracing(remoteMonitoringServiceParams);\n```\n\n### CLI usage\n\nIn order to upload sourcemaps to remote monitoring service you can use cli from this package like `web-app-monitoring__upload-sourcemaps` as shown below\n\n```json\n{\n  \"scripts\": {\n    \"upload:sourcemaps\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 npm run build && MONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\"\n  }\n}\n```\n\n- `MONITORING_TOOL__BUILD_DIR` - relative path to bulid folder, where sourcemaps are located\n- `MONITORING_TOOL__PUBLIC_PATH` - relative public path to js files, when code is served in production\n\n## Roadmap\n\n- write guide on using instrumentation hook for nextjs server integration\n- write guide on env variables renaming\n- add guide on next integration (TS, global variables, phase in next.config.js)\n","readmeFilename":"README.md","gitHead":"f5b1fbac76ac401250d9c05a4dc59c7ec59b4cb0","description":"[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)","_id":"@kilohealth/web-app-monitoring@1.0.0-alpha.10","_nodeVersion":"18.16.0","_npmVersion":"9.6.7","dist":{"integrity":"sha512-MPYCGUddohKwinarox09PEDuxJ7sD7MNqPRaIAhHW5lV6W6eRRRf4IeS13bl7ECNbDXoR0YwpXpybET5v42ing==","shasum":"0a770e451df507f0b94ee3d6b8f38f0bbd038207","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.0.0-alpha.10.tgz","fileCount":60,"unpackedSize":69491,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFMjeXVa6QaxQY0n6+O3umDSIm9/wn1HYo6U2IRqGB3EAiEA7BM9/ASpiAxf5rprfngZzuhDL4kZ3f7Xh/Wh52T5Zeg="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.0.0-alpha.10_1685447146716_0.6511543870034839"},"_hasShrinkwrap":false},"1.0.0-alpha.11":{"name":"@kilohealth/web-app-monitoring","version":"1.0.0-alpha.11","license":"MIT","author":{"name":"Kilo Health"},"main":"dist/index.js","bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","prepare":"husky install","semantic-release":"semantic-release","test":"jest","upload:sourcemaps":"./dist/cli/sourcemap-uploader.sh"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","@datadog/datadog-ci":"^2.11.0","dd-trace":"^4.0.0","lodash":"^4.17.21","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","@types/lodash":"^4.14.195","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jest-environment-jsdom":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"readme":"# @frontend/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\nThis package is for monitoring and error tracking.\nIt consists of 3 parts - browser, server and cli.\n\n## Table of Contents\n\n1. [Getting started](#getting-started)\n2. [Usage in project](#usage-in-project)\n3. [Methods](#methods)\n\n## Getting started\n\nAdd dependency\n\n```\n$ npm install @frontend/web-app-monitoring\n```\n\n## Usage in project\n\n### Browser or Server Methods\n\n- `debug, info, warn, error`: adds item to logs (either local or remote, depending on setup);\n- `reportError`: reports error to monitoring system (either local or remote, depending on setup);\n\n```js\nimport { BrowserMonitoringService } from '@frontend/web-app-monitoring';\n\nexport const localMonitoring = new BrowserMonitoringService();\nlocalMonitoring.info('Smth happened');\n```\n\nor\n\n```js\nimport { ServerMonitoringService } from '@frontend/web-app-monitoring';\n\nconst serverMonitoringService = new ServerMonitoringService({\n  serviceName: 'serviceName',\n  serviceEnv: 'serviceEnv',\n  serviceVersion: 'serviceVersion',\n  clientToken: 'clientToken',\n});\nserverMonitoringService.info('Smth happened');\n```\n\n### Server Only\n\n- `initServerMonitoring`: creates serverMonitoringService under the hood and can do additional utility functions.\n  Returns instance of serverMonitoringService.\n\n```js\nimport { initServerMonitoring } from '@frontend/web-app-monitoring';\n\nconst remoteMonitoringServiceParams = {\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n};\ninitServerMonitoring(remoteMonitoringServiceParams, {\n  shouldOverrideNativeConsole: true,\n  shouldCatchProcessErrors: true,\n  globalMonitoringInstanceName: 'kiloServerMonitoring',\n});\n```\n\n#### Params:\n\n- `shouldOverrideNativeConsole` - defaults to false. When true - service will override native console with itself.\n- `shouldCatchProcessErrors` - defaults to false. When true - service will subscribe to native errors and report them\n- `globalMonitoringInstanceName` - defaults to empty string. When provided - service will put itself into global node scope under provided name. So it can be accessed in other parts of the code.\n\n- `initTracing`: initiate tracing of application. Need to be provided with remote monitoring system params.\n  Returns instance of tracer.\n\n```js\nimport { initTracing } from '@frontend/web-app-monitoring';\n\nconst remoteMonitoringServiceParams = {\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n};\ninitTracing(remoteMonitoringServiceParams);\n```\n\n### CLI usage\n\nIn order to upload sourcemaps to remote monitoring service you can use cli from this package like `web-app-monitoring__upload-sourcemaps` as shown below\n\n```json\n{\n  \"scripts\": {\n    \"upload:sourcemaps\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 npm run build && MONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\"\n  }\n}\n```\n\n- `MONITORING_TOOL__BUILD_DIR` - relative path to bulid folder, where sourcemaps are located\n- `MONITORING_TOOL__PUBLIC_PATH` - relative public path to js files, when code is served in production\n\n## Roadmap\n\n- write guide on using instrumentation hook for nextjs server integration\n- write guide on env variables renaming\n- add guide on next integration (TS, global variables, phase in next.config.js)\n","readmeFilename":"README.md","gitHead":"53229dc7493173f1a9f1ad0c0802ecea5c5349cf","description":"[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)","_id":"@kilohealth/web-app-monitoring@1.0.0-alpha.11","_nodeVersion":"18.16.0","_npmVersion":"9.6.7","dist":{"integrity":"sha512-2RtlKcuxyM0oqDgjcpXT4+slxqprWH1Y8SFxATUAbv3O2M1m6XVlsVxm/a0xUtKH5gG4rl0eACjC13ZmDId4zg==","shasum":"a65aa9033b3f8ab1a4c4a4dbb16cd3da62b85ca6","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.0.0-alpha.11.tgz","fileCount":60,"unpackedSize":69895,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCXgBX3OWHagBaUlkIqkU4m5xsGQ+NtfUwoViH272JN1gIhAIAYDhMWPPtffojGpWEzlCQEYNTGKTouSZcX/YwV38yu"}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.0.0-alpha.11_1685532272183_0.8454035599270635"},"_hasShrinkwrap":false},"1.0.0-alpha.12":{"name":"@kilohealth/web-app-monitoring","version":"1.0.0-alpha.12","license":"MIT","author":{"name":"Kilo Health"},"main":"dist/index.js","bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","prepare":"husky install","semantic-release":"semantic-release","test":"jest","upload:sourcemaps":"./dist/cli/sourcemap-uploader.sh"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","@datadog/datadog-ci":"^2.11.0","dd-trace":"^4.0.0","lodash":"^4.17.21","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","@types/lodash":"^4.14.195","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jest-environment-jsdom":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"readme":"# @frontend/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\nThis package is for monitoring and error tracking.\nIt consists of 3 parts - browser, server and cli.\n\n## Table of Contents\n\n1. [Getting started](#getting-started)\n2. [Usage in project](#usage-in-project)\n3. [Methods](#methods)\n\n## Getting started\n\nAdd dependency\n\n```\n$ npm install @frontend/web-app-monitoring\n```\n\n## Usage in project\n\n### Browser or Server Methods\n\n- `debug, info, warn, error`: adds item to logs (either local or remote, depending on setup);\n- `reportError`: reports error to monitoring system (either local or remote, depending on setup);\n\n```js\nimport { BrowserMonitoringService } from '@frontend/web-app-monitoring';\n\nexport const localMonitoring = new BrowserMonitoringService();\nlocalMonitoring.info('Smth happened');\n```\n\nor\n\n```js\nimport { ServerMonitoringService } from '@frontend/web-app-monitoring';\n\nconst serverMonitoringService = new ServerMonitoringService({\n  serviceName: 'serviceName',\n  serviceEnv: 'serviceEnv',\n  serviceVersion: 'serviceVersion',\n  clientToken: 'clientToken',\n});\nserverMonitoringService.info('Smth happened');\n```\n\n### Server Only\n\n- `initServerMonitoring`: creates serverMonitoringService under the hood and can do additional utility functions.\n  Returns instance of serverMonitoringService.\n\n```js\nimport { initServerMonitoring } from '@frontend/web-app-monitoring';\n\nconst remoteMonitoringServiceParams = {\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n};\ninitServerMonitoring(remoteMonitoringServiceParams, {\n  shouldOverrideNativeConsole: true,\n  shouldCatchProcessErrors: true,\n  globalMonitoringInstanceName: 'kiloServerMonitoring',\n});\n```\n\n#### Params:\n\n- `shouldOverrideNativeConsole` - defaults to false. When true - service will override native console with itself.\n- `shouldCatchProcessErrors` - defaults to false. When true - service will subscribe to native errors and report them\n- `globalMonitoringInstanceName` - defaults to empty string. When provided - service will put itself into global node scope under provided name. So it can be accessed in other parts of the code.\n\n- `initTracing`: initiate tracing of application. Need to be provided with remote monitoring system params.\n  Returns instance of tracer.\n\n```js\nimport { initTracing } from '@frontend/web-app-monitoring';\n\nconst remoteMonitoringServiceParams = {\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n};\ninitTracing(remoteMonitoringServiceParams);\n```\n\n### CLI usage\n\nIn order to upload sourcemaps to remote monitoring service you can use cli from this package like `web-app-monitoring__upload-sourcemaps` as shown below\n\n```json\n{\n  \"scripts\": {\n    \"upload:sourcemaps\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 npm run build && MONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\"\n  }\n}\n```\n\n- `MONITORING_TOOL__BUILD_DIR` - relative path to bulid folder, where sourcemaps are located\n- `MONITORING_TOOL__PUBLIC_PATH` - relative public path to js files, when code is served in production\n\n## Roadmap\n\n- write guide on using instrumentation hook for nextjs server integration\n- write guide on env variables renaming\n- add guide on next integration (TS, global variables, phase in next.config.js)\n","readmeFilename":"README.md","gitHead":"0680c4d8f309b9dc3657d9bb5d818e130152a06d","description":"[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)","_id":"@kilohealth/web-app-monitoring@1.0.0-alpha.12","_nodeVersion":"18.16.0","_npmVersion":"9.6.7","dist":{"integrity":"sha512-sZONuB1n5Y8cambdWNsDXVPC5lgR2Lm+xL91IrxXzfVfE09Rc7C12bR6fOrm/rB+WdzvByCsS55C98rMLjSebA==","shasum":"8778b9b615d08b6c9b15958bb5d3d298a0edce5f","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.0.0-alpha.12.tgz","fileCount":60,"unpackedSize":72302,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDg9RCBSq3kR2hBCzmLl1mstdoct7FzPzKK62URLGS/6QIhANGX9Rbu1NX8Y9XIXLmhqOGhj9lz0X46z7Cu2UPXwBjw"}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.0.0-alpha.12_1685680725981_0.7020406569239357"},"_hasShrinkwrap":false},"1.0.0-alpha.13":{"name":"@kilohealth/web-app-monitoring","version":"1.0.0-alpha.13","license":"MIT","author":{"name":"Kilo Health"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./client":{"import":{"types":"./dist/client/index.d.ts","default":"./dist/client/index.js"},"require":{"types":"./dist/client/index.d.ts","default":"./dist/client/index.js"}}},"main":"./dist/index.js","types":"./dist/index.d.ts","bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","prepare":"husky install","semantic-release":"semantic-release","test":"jest","upload:sourcemaps":"./dist/cli/sourcemap-uploader.sh"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","@datadog/datadog-ci":"^2.11.0","dd-trace":"^4.0.0","deepmerge":"^4.3.1","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","@types/lodash":"^4.14.195","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jest-environment-jsdom":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"readme":"# @frontend/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\n## The Idea\n\nPackage was created to abstract away underlying level of monitoring,\nto make it easy to setup monitoring for any env as well as to make it easy to migrate from DataDog to other monitoring solution.\n\nThe package consists of 3 parts:\n\n- browser monitoring\n- CLI (needed to upload sourcemaps for browser monitoring)\n- server monitoring\n\n## Browser monitoring setup:\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n> If you migrating from direct datadog integration - don’t forget to remove @datadog/browser-logs and @datadog/datadog-ci. Those are now deps of @kilohealth/web-app-monitoring.\n\n```\nnpm uninstall @datadog/browser-logs @datadog/datadog-ci\n```\n\n### Add build phase env variables\n\n- `MONITORING_TOOL__API_KEY` - this key is needed in order to uploaded sourcemaps for browser monitoring. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048).\n- `MONITORING_TOOL__SERVICE_NAME`, `MONITORING_TOOL__SERVICE_VERSION` and `MONITORING_TOOL__SERVICE_ENV`- the service name, version and env. These variables will be used by client code as well as by cli. Most client frameworks will not expose all node build phase env vars, so you probably need to reexpose them with prefix to switch on automatic replacement for client code during client build. In particular\n  - For NextJS - you have to add prefix `NEXT_PUBLIC_` to each of them. For example you have to add not only `MONITORING_TOOL__SERVICE_NAME=timely-hand-web-funnel-app` but also `NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME`. See more in [docs](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#exposing-environment-variables-to-the-browser).\n  - For GatsbyJS - you have to add prefix `GATSBY_`. See more in [docs](https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/#accessing-environment-variables-in-the-browser)\n  - For ViteJS - you have to add prefix `VITE_`. See more in [docs](https://vitejs.dev/guide/env-and-mode.html#env-files).\n- `MONITORING_TOOL__CLIENT_TOKEN` - this is client side token, which need to be built into client code in order to send logs into DD server.\n  Because it is needed on client you will have to re-expose it using same approach as variables above (probably prefixing env var).\n  Token you can create or find [here](https://app.datadoghq.com/organization-settings/client-tokens).\n  > PS: theoretically you can avoid creating `MONITORING_TOOL__CLIENT_TOKEN` env variable and create only `NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN` instead, because this variable is not needed for CLI to work. But for the sake of SRP we advocate for sticking with same reexposing approach here.\n\n#### Example of full env variables setup for client monitoring:\n\n##### Expose client token to be able to reexpose it for client-side code:\n\n```\nMONITORING_TOOL__CLIENT_TOKEN=pub2_your_client_token\n```\n\n##### Set variables for source map upload CLI to work during the build phase:\n\n```\nMONITORING_TOOL__SERVICE_ENV=$CI_ENVIRONMENT_NAME\n```\n\n```\nMONITORING_TOOL__SERVICE_NAME=greantess-funnel\n```\n\n```\nMONITORING_TOOL__SERVICE_VERSION=$CI_COMMIT_SHA\n```\n\n```\nMONITORING_TOOL__API_KEY=4be_your_api_key\n```\n\n##### Reexpose for your framework to client-side code(NextJS example):\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV=$MONITORING_TOOL__SERVICE_ENV\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION=$MONITORING_TOOL__SERVICE_VERSION\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN=$MONITORING_TOOL__CLIENT_TOKEN\n```\n\n### Modify your build code to generate sourcemaps, depending on env variable\n\nIdeally we don't want to generate and upload sourcemaps during each build.\nIn order to opt-in for this behavior sometimes we need to make additional configuration changes in our build process.\nWe need to build sourcemaps only in case specific env variable `IS_SOURCEMAP_UPLOAD_BUILD` is provided.\nWe don't provide it for dev or debug builds, only for production.\nThese are articles on how to do this for different frameworks and examples.\n\n- [Vite](https://vitejs.dev/config/build-options.html#build-sourcemap)\n\n```\nexport default defineConfig({\n  ...\n  build: {\n    sourcemap: Boolean(process.env.IS_SOURCEMAP_UPLOAD_BUILD),\n    ...\n  }\n  ...\n})\n```\n\n- [NextJS](https://nextjs.org/docs/pages/api-reference/next-config-js/productionBrowserSourceMaps)\n\n```\nmodule.exports = {\n  ...\n  productionBrowserSourceMaps: Boolean(process.env.IS_SOURCEMAP_UPLOAD_BUILD),\n  ...\n}\n```\n\n- Gatsby is a little bit more tricky. It generates sourcemaps by default. In order to prevent this you can add code to\n\n```\nmodule.exports.onCreateWebpackConfig = ({ stage, actions }) => {\n  // build-javascript is prod build phase\n  if (stage === 'build-javascript') {\n    actions.setWebpackConfig({\n      // hidden-source-map removes last line from final files,\n      // to avoid contenthash mismatch between builds\n      // we don't want sourcemaps in prod by default\n      devtool: process.env.IS_SOURCEMAP_UPLOAD_BUILD\n        ? 'hidden-source-map'\n        : false,\n    });\n  }\n};\n```\n\nThere is also an [article](https://akashrajpurohit.com/blog/disable-source-maps-in-gatsbyjs-v2/) with more details.\n\n### Add build and upload sourcemaps script to scripts section\n\n#### Prepare sourcemap upload build\n\nIt should run bin from our lib called `web-app-monitoring__upload-sourcemaps`.\nFor this script to work you would need to provide it with two variables\n\n- `MONITORING_TOOL__PUBLIC_PATH` - this is RELATIVE path, part of URL between domain (which can be different for different environments) and path to file itself.\n  In other words - base path for all the assets within your application.\n  You can think of this as kind of relative [Public Path | webpack](https://webpack.js.org/guides/public-path/).\n  For example it can be `/` or `/static`.\n  In other words this is common relative prefix for all your static files or / if there is none.\n  - for Vite default is `/`\n  - for NextJS default is `/_next/static/chunks` (!!! `_` instead of `.` in file system)\n  - for GatsbyJS default is `/`\n- `MONITORING_TOOL__BUILD_DIR` - this should be RELATIVE path to your build directory. For example `./dist` or `./build`.\n  - for Vite default is `./dist`\n  - for NextJS default is `./.next/static/chunks`\n  - for GatsbyJS default is `./public`\n\nExample for NextJS\n\n```\nMONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\n```\n\n#### 1. Approach with parallel build\n\nWe advocate for this approach.\nWith it you have separate script to build code for deployment and another one to make a build with sourcemaps to upload those to monitoring tool.\nWe decided to extract source map building and uploading into separate step because:\n\n- not each build may need these, and it will increase build time. For example, you may want to avoid this for dev builds.\n- we don’t want to manually alter the build (aka removing sourcemaps from it) because it is fragile and hard to maintain\n- we don’t want sourcemaps to leak into production, so we wanna separate generating them and uploading files into prod into different processes\n\nExample for NextJS\n\n```\n\"upload:sourcemaps\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 npm run build && MONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\"\n```\n\nAnd then your CI should run `upload:sourcemaps` script in parallel with main build,\nto avoid blocking and increasing main build time.\n\n#### 2. Approach with same build\n\nThere is alternative approach to tweak main build process with sourcemaps.\nWe need to do next things:\n\n- switch sourcemaps to be hidden.\n  For example instead of using option sourcemaps use hidden-sourcemaps.\n  With this we will avoid warning in console in production regarding the fact that files have a reference to sourcemaps but sourcemaps are not found.\n  We are just removing this reference during build phase.\n\n- extend usual build script with flag to include sourcemaps `IS_SOURCEMAP_UPLOAD_BUILD`,\n  script to upload them and script to remove them. For example\n\n```\n\"only-upload:sourcemaps\": \"MONITORING_TOOL__BUILD_DIR=./public MONITORING_TOOL__PUBLIC_PATH=/ web-app-monitoring__upload-sourcemaps\",\n\"remove:sourcemaps\": \"find ./public -name \\\"*.map\\\" -type f -delete\",\n\"build\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 gatsby build --prefix-paths && npm run only-upload:sourcemaps && npm run remove:sourcemaps\",\n```\n\n### Usage: Import and instantiate BrowserMonitoringService\n\n```\nimport { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/dist/browser';\n\nexport const monitoring = new BrowserMonitoringService({\n  authToken: NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN,\n  serviceName: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV,\n})\n```\n\nAs you can see we are using here all our exposed variables. If any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n#### OPTIONAL: If you are using React you may benefit from utilizing [Error Boundaries](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary):\n\n```\nimport React, { Component, PropsWithChildren } from 'react';\nimport { monitoring } from '../services/monitoring';\n\ninterface ErrorBoundaryProps {}\ninterface ErrorBoundaryState {\n  hasError: boolean;\n}\n\nexport class ErrorBoundary extends Component<\n  PropsWithChildren<ErrorBoundaryProps>,\n  ErrorBoundaryState\n> {\n  static getDerivedStateFromError(_: Error): ErrorBoundaryState {\n    return { hasError: true };\n  }\n\n  state: ErrorBoundaryState = {\n    hasError: false,\n  };\n\n  componentDidCatch(error: Error) {\n    monitoring.reportError(error);\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return <h1>Sorry... There was an error</h1>;\n    }\n\n    return this.props.children;\n  }\n}\n```\n\n## Server monitoring setup (NextJS):\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n### Add build phase env variables\n\n- `MONITORING_TOOL__API_KEY` - this key is needed in order to send logs. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048).\n- `MONITORING_TOOL__SERVICE_NAME`, `MONITORING_TOOL__SERVICE_VERSION` and `MONITORING_TOOL__SERVICE_ENV` - the service name, version and env. These variables will be used to send logs.\n\n#### Example of full env variables setup for server monitoring:\n\n##### Set variables for sending logs:\n\n```\nMONITORING_TOOL__SERVICE_ENV=$CI_ENVIRONMENT_NAME\n```\n\n```\nMONITORING_TOOL__SERVICE_NAME=greantess-funnel\n```\n\n```\nMONITORING_TOOL__SERVICE_VERSION=$CI_COMMIT_SHA\n```\n\n```\nMONITORING_TOOL__API_KEY=4be_your_api_key\n```\n\n### Usage\n\n#### Approach with facade\n\n`initServerMonitoring` is a facade over `ServerMonitoringService`.\nYou can instantiate and use that service directly.\nThis function basically does one thing - instantiate it and can do 3 more additional things:\n\n- call `overrideNativeConsole` - method of the service to override native console, to log to datadog instead.\n- cal `catchProcessErrors` - method of the service to subscribe to native errors, to log them to datadog.\n- put service itself into global scope under defined name, so serverside code can use it.\n\nYou may wonder why we instantiate service here and not in server-side code.\nThe reason for that is if we override the native console and catch native errors - we would like to set up this as soon as possible.\nIf you don’t care too much about the very first seconds of next server - you can use alternative simpler server side logging solution.\n\nExample(for NextJS):\n\n- update next.config.ts to include into start script of production server code\n  `next.config.ts:`\n\n```\nconst { initServerMonitoring } = require('@kilohealth/web-app-monitoring/dist/server');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    const remoteMonitoringServiceParams = {\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    };\n    config = {\n      shouldOverrideNativeConsole: true,\n      shouldCatchProcessErrors: true,\n      globalMonitoringInstanceName: 'kiloServerMonitoring',\n    }\n    initServerMonitoring(remoteMonitoringServiceParams, config);\n  }\n}\n```\n\n- update `custom.d.ts` file to declare that global scope now have monitoring service as a prop\n  In order to use ServerMonitoringService instance in other parts of code via global we need to let TS know\n  that we added new property to global object.\n  In NextJS you can just create or add next code into `custom.d.ts` file in root of the project.\n  Be aware that var name matches string that you provided in code above (kiloServerMonitoring in this case).\n  `custom.d.ts:`\n\n```\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\ndeclare global {\n  // eslint-disable-next-line no-var\n  var kiloServerMonitoring: ServerMonitoringService;\n}\n```\n\n- use it in code\n\n```\nexport const getHomeServerSideProps = async context => {\n  global.kiloServerMonitoring.info('getHomeServerSideProps called');\n  ...\n};\n```\n\n#### Approach with direct instantiation\n\nIf you don’t care too much about catching native errors or native logs\nin the early stages of your server app -\nyou can avoid sharing logger via global scope and instead initialize it inside of app.\n\n```\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\nexport const monitoring = new ServerMonitoringService({\n  authToken: MONITORING_TOOL__API_KEY,\n  serviceName: MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: MONITORING_TOOL__SERVICE_ENV,\n})\n```\n\nAs you can see we are using here all our env variables.\nIf any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n### Tracing setup:\n\nWe need to connect tracing as soon as possible during code, so it can be injected into all base modules for APM monitoring.\nTracing module is available via:\n\n```\nconst { initTracing } = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\ninitTracing({\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n});\n```\n\nExample(NextJS):\n\n```\nconst { PHASE_PRODUCTION_SERVER } = require('next/constants');\nconst { initTracing } = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    initTracing({\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    });\n  }\n}\n```\n\n> In newer versions of NextJS there is experimental feature called\n> [instrumentationHook](https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation)\n> We can opt out from using undocumented `PHASE_PRODUCTION_SERVER` to use `instrumentationHook` for tracing init.\n\n> There is also possibility to use `NODE_OPTIONS='-r ./prestart-script.js ' next start` instead.\n> But there is an issue with `pino-datadog-transport`, which for performance reason spawns separate thread for log sending to data-dog and it this option seems to be passed to that process as well which triggers an infinite loop of require and initialization.\n\n## API\n\n### MonitoringService (both BrowserMonitoringService and ServerMonitoringService have these methods)\n\n#### debug, info, warn\n\n```\ndebug(message: string, context?: object)\ninfo(message: string, context?: object)\nwarn(message: string, context?: object)\n```\n\n- `message` - any message to be logged\n- `context` - object with all needed and related to the log entrance data\n\n#### error\n\n```\nerror(message: string, context?: object, error?: Error)\n```\n\nSame as above, but you can also optionally pass error instance as thrid parameter\n\n#### reportError\n\n```\nreportError(error: Error, context?: object)\n```\n\nShortcut for `service.error()`, which uses `error.message` field as message param for `error` method.\n\n### BrowserMonitoringService\n\n#### constructor\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```\ninterface RemoteMonitoringServiceParams {\n  serviceName?: string;\n  serviceVersion?: string;\n  serviceEnv?: string;\n  authToken?: string;\n}\n```\n\n- `RemoteMonitoringServiceConfig` - datadog params passed to init function. More info in [docs](https://docs.datadoghq.com/logs/log_collection/javascript/#initialization-parameters)\n- `serviceName` - name of the service\n- `serviceVersion` - version of the service\n- `serviceEnv` - environment where service is deployed\n- `authToken` - client token\n\n### ServerMonitoringService\n\n#### constructor\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```\ninterface RemoteMonitoringServiceConfig {\n  transportOptions?: Partial<TransportBaseOptions>;\n  loggerOptions?: Partial<LoggerOptions>;\n}\n```\n\n- `transportOptions` - [pino-datadog-transport options](https://github.com/theogravity/pino-datadog-transport#configuration-options)\n- `loggerOptions` - [pino logger options](https://getpino.io/#/docs/api?id=options-object)\n\n#### overrideLogger\n\nOverrides logger passed as argument with monitoring logger.\nAll methods of this logger will be overridden with corresponding methods of server monitoring.\n\n```\noverrideLogger(unknownLogger: UnknownLogger)\n```\n\n```\ninterface UnknownLogger {\n  log?(...parts: unknown[]): void;\n  debug?(...parts: unknown[]): void;\n  info(...parts: unknown[]): void;\n  warn(...parts: unknown[]): void;\n  error(...parts: unknown[]): void;\n}\n```\n\n#### overrideNativeConsole\n\nCalls overrideLogger for native console.\n\n```\noverrideNativeConsole()\n```\n\n#### catchProcessErrors\n\nSubscribes to `unhandledRejection` and `uncaughtException` events of the process to report `error` in such cases.\n\n```\ncatchProcessErrors()\n```\n\n### ServerMonitoringService\n\n#### initServerMonitoring\n\nInstantiate ServerMonitoringService with provided params and may also do additional work,\ndepending on provided variables.\n\n```\ninitServerMonitoring = (\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  monitoringOptions?: MonitoringOptions,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig\n): ServerMonitoringService\n```\n\n```\ninterface MonitoringOptions {\n  shouldOverrideNativeConsole?: boolean;\n  shouldCatchProcessErrors?: boolean;\n  globalMonitoringInstanceName?: string;\n}\n```\n\n- `RemoteMonitoringServiceParams` and `RemoteMonitoringServiceConfig` are same as in `constructor` api\n- `shouldOverrideNativeConsole` - if `true`, will call `serverMonitoringService.overrideNativeConsole()` under the hood\n- `shouldCatchProcessErrors` - if `true`, will call `serverMonitoringService.catchProcessErrors()` under the hood\n- `globalMonitoringInstanceName` - if provided with non-empty string will put instantiated `serverMonitoringService` into global scope under provided name.\n","readmeFilename":"README.md","gitHead":"15520f019ae1974b3d2f1eef1ad41e0c7e52d8e8","description":"[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)","_id":"@kilohealth/web-app-monitoring@1.0.0-alpha.13","_nodeVersion":"18.16.0","_npmVersion":"9.6.7","dist":{"integrity":"sha512-QR4sEfiOLGmpp87/JgcoaDntLUG5OFmBVjwgczRMe5RjLPA1fvnJWR8I1s9Oc0hhfLbxa3vY6ddq/FqCKWW5Fg==","shasum":"92d8dc35a0e60c37d56f84c554b2b9abd829c72c","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.0.0-alpha.13.tgz","fileCount":40,"unpackedSize":52916,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCxX4X+l4KqauyGUu1EwBOSCgNa26ct6f1FdkDfglUeGwIgLpwRQBCBeX9lqGAs3mZMbGNY6uxR18I5IbipFRyjyF4="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.0.0-alpha.13_1686891738428_0.614038250213113"},"_hasShrinkwrap":false},"1.0.0-alpha.14":{"name":"@kilohealth/web-app-monitoring","version":"1.0.0-alpha.14","license":"MIT","author":{"name":"Kilo Health"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"main":"./dist/index.js","types":"./dist/index.d.ts","bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","prepare":"husky install","semantic-release":"semantic-release","test":"jest","upload:sourcemaps":"./dist/cli/sourcemap-uploader.sh"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","@datadog/datadog-ci":"^2.11.0","dd-trace":"^4.0.0","deepmerge":"^4.3.1","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","@types/lodash":"^4.14.195","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jest-environment-jsdom":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"readme":"# @frontend/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\n## The Idea\n\nPackage was created to abstract away underlying level of monitoring,\nto make it easy to setup monitoring for any env as well as to make it easy to migrate from DataDog to other monitoring solution.\n\nThe package consists of 3 parts:\n\n- browser monitoring\n- CLI (needed to upload sourcemaps for browser monitoring)\n- server monitoring\n\n## Browser monitoring setup:\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n> If you migrating from direct datadog integration - don’t forget to remove @datadog/browser-logs and @datadog/datadog-ci. Those are now deps of @kilohealth/web-app-monitoring.\n\n```\nnpm uninstall @datadog/browser-logs @datadog/datadog-ci\n```\n\n### Add build phase env variables\n\n- `MONITORING_TOOL__API_KEY` - this key is needed in order to uploaded sourcemaps for browser monitoring. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048).\n- `MONITORING_TOOL__SERVICE_NAME`, `MONITORING_TOOL__SERVICE_VERSION` and `MONITORING_TOOL__SERVICE_ENV`- the service name, version and env. These variables will be used by client code as well as by cli. Most client frameworks will not expose all node build phase env vars, so you probably need to reexpose them with prefix to switch on automatic replacement for client code during client build. In particular\n  - For NextJS - you have to add prefix `NEXT_PUBLIC_` to each of them. For example you have to add not only `MONITORING_TOOL__SERVICE_NAME=timely-hand-web-funnel-app` but also `NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME`. See more in [docs](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#exposing-environment-variables-to-the-browser).\n  - For GatsbyJS - you have to add prefix `GATSBY_`. See more in [docs](https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/#accessing-environment-variables-in-the-browser)\n  - For ViteJS - you have to add prefix `VITE_`. See more in [docs](https://vitejs.dev/guide/env-and-mode.html#env-files).\n- `MONITORING_TOOL__CLIENT_TOKEN` - this is client side token, which need to be built into client code in order to send logs into DD server.\n  Because it is needed on client you will have to re-expose it using same approach as variables above (probably prefixing env var).\n  Token you can create or find [here](https://app.datadoghq.com/organization-settings/client-tokens).\n  > PS: theoretically you can avoid creating `MONITORING_TOOL__CLIENT_TOKEN` env variable and create only `NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN` instead, because this variable is not needed for CLI to work. But for the sake of SRP we advocate for sticking with same reexposing approach here.\n\n#### Example of full env variables setup for client monitoring:\n\n##### Expose client token to be able to reexpose it for client-side code:\n\n```\nMONITORING_TOOL__CLIENT_TOKEN=pub2_your_client_token\n```\n\n##### Set variables for source map upload CLI to work during the build phase:\n\n```\nMONITORING_TOOL__SERVICE_ENV=$CI_ENVIRONMENT_NAME\n```\n\n```\nMONITORING_TOOL__SERVICE_NAME=greantess-funnel\n```\n\n```\nMONITORING_TOOL__SERVICE_VERSION=$CI_COMMIT_SHA\n```\n\n```\nMONITORING_TOOL__API_KEY=4be_your_api_key\n```\n\n##### Reexpose for your framework to client-side code(NextJS example):\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV=$MONITORING_TOOL__SERVICE_ENV\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION=$MONITORING_TOOL__SERVICE_VERSION\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN=$MONITORING_TOOL__CLIENT_TOKEN\n```\n\n### Modify your build code to generate sourcemaps, depending on env variable\n\nIdeally we don't want to generate and upload sourcemaps during each build.\nIn order to opt-in for this behavior sometimes we need to make additional configuration changes in our build process.\nWe need to build sourcemaps only in case specific env variable `IS_SOURCEMAP_UPLOAD_BUILD` is provided.\nWe don't provide it for dev or debug builds, only for production.\nThese are articles on how to do this for different frameworks and examples.\n\n- [Vite](https://vitejs.dev/config/build-options.html#build-sourcemap)\n\n```\nexport default defineConfig({\n  ...\n  build: {\n    sourcemap: Boolean(process.env.IS_SOURCEMAP_UPLOAD_BUILD),\n    ...\n  }\n  ...\n})\n```\n\n- [NextJS](https://nextjs.org/docs/pages/api-reference/next-config-js/productionBrowserSourceMaps)\n\n```\nmodule.exports = {\n  ...\n  productionBrowserSourceMaps: Boolean(process.env.IS_SOURCEMAP_UPLOAD_BUILD),\n  ...\n}\n```\n\n- Gatsby is a little bit more tricky. It generates sourcemaps by default. In order to prevent this you can add code to\n\n```\nmodule.exports.onCreateWebpackConfig = ({ stage, actions }) => {\n  // build-javascript is prod build phase\n  if (stage === 'build-javascript') {\n    actions.setWebpackConfig({\n      // hidden-source-map removes last line from final files,\n      // to avoid contenthash mismatch between builds\n      // we don't want sourcemaps in prod by default\n      devtool: process.env.IS_SOURCEMAP_UPLOAD_BUILD\n        ? 'hidden-source-map'\n        : false,\n    });\n  }\n};\n```\n\nThere is also an [article](https://akashrajpurohit.com/blog/disable-source-maps-in-gatsbyjs-v2/) with more details.\n\n### Add build and upload sourcemaps script to scripts section\n\n#### Prepare sourcemap upload build\n\nIt should run bin from our lib called `web-app-monitoring__upload-sourcemaps`.\nFor this script to work you would need to provide it with two variables\n\n- `MONITORING_TOOL__PUBLIC_PATH` - this is RELATIVE path, part of URL between domain (which can be different for different environments) and path to file itself.\n  In other words - base path for all the assets within your application.\n  You can think of this as kind of relative [Public Path | webpack](https://webpack.js.org/guides/public-path/).\n  For example it can be `/` or `/static`.\n  In other words this is common relative prefix for all your static files or / if there is none.\n  - for Vite default is `/`\n  - for NextJS default is `/_next/static/chunks` (!!! `_` instead of `.` in file system)\n  - for GatsbyJS default is `/`\n- `MONITORING_TOOL__BUILD_DIR` - this should be RELATIVE path to your build directory. For example `./dist` or `./build`.\n  - for Vite default is `./dist`\n  - for NextJS default is `./.next/static/chunks`\n  - for GatsbyJS default is `./public`\n\nExample for NextJS\n\n```\nMONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\n```\n\n#### 1. Approach with parallel build\n\nWe advocate for this approach.\nWith it you have separate script to build code for deployment and another one to make a build with sourcemaps to upload those to monitoring tool.\nWe decided to extract source map building and uploading into separate step because:\n\n- not each build may need these, and it will increase build time. For example, you may want to avoid this for dev builds.\n- we don’t want to manually alter the build (aka removing sourcemaps from it) because it is fragile and hard to maintain\n- we don’t want sourcemaps to leak into production, so we wanna separate generating them and uploading files into prod into different processes\n\nExample for NextJS\n\n```\n\"upload:sourcemaps\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 npm run build && MONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\"\n```\n\nAnd then your CI should run `upload:sourcemaps` script in parallel with main build,\nto avoid blocking and increasing main build time.\n\n#### 2. Approach with same build\n\nThere is alternative approach to tweak main build process with sourcemaps.\nWe need to do next things:\n\n- switch sourcemaps to be hidden.\n  For example instead of using option sourcemaps use hidden-sourcemaps.\n  With this we will avoid warning in console in production regarding the fact that files have a reference to sourcemaps but sourcemaps are not found.\n  We are just removing this reference during build phase.\n\n- extend usual build script with flag to include sourcemaps `IS_SOURCEMAP_UPLOAD_BUILD`,\n  script to upload them and script to remove them. For example\n\n```\n\"only-upload:sourcemaps\": \"MONITORING_TOOL__BUILD_DIR=./public MONITORING_TOOL__PUBLIC_PATH=/ web-app-monitoring__upload-sourcemaps\",\n\"remove:sourcemaps\": \"find ./public -name \\\"*.map\\\" -type f -delete\",\n\"build\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 gatsby build --prefix-paths && npm run only-upload:sourcemaps && npm run remove:sourcemaps\",\n```\n\n### Usage: Import and instantiate BrowserMonitoringService\n\n```\nimport { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/dist/browser';\n\nexport const monitoring = new BrowserMonitoringService({\n  authToken: NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN,\n  serviceName: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV,\n})\n```\n\nAs you can see we are using here all our exposed variables. If any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n#### OPTIONAL: If you are using React you may benefit from utilizing [Error Boundaries](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary):\n\n```\nimport React, { Component, PropsWithChildren } from 'react';\nimport { monitoring } from '../services/monitoring';\n\ninterface ErrorBoundaryProps {}\ninterface ErrorBoundaryState {\n  hasError: boolean;\n}\n\nexport class ErrorBoundary extends Component<\n  PropsWithChildren<ErrorBoundaryProps>,\n  ErrorBoundaryState\n> {\n  static getDerivedStateFromError(_: Error): ErrorBoundaryState {\n    return { hasError: true };\n  }\n\n  state: ErrorBoundaryState = {\n    hasError: false,\n  };\n\n  componentDidCatch(error: Error) {\n    monitoring.reportError(error);\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return <h1>Sorry... There was an error</h1>;\n    }\n\n    return this.props.children;\n  }\n}\n```\n\n## Server monitoring setup (NextJS):\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n### Add build phase env variables\n\n- `MONITORING_TOOL__API_KEY` - this key is needed in order to send logs. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048).\n- `MONITORING_TOOL__SERVICE_NAME`, `MONITORING_TOOL__SERVICE_VERSION` and `MONITORING_TOOL__SERVICE_ENV` - the service name, version and env. These variables will be used to send logs.\n\n#### Example of full env variables setup for server monitoring:\n\n##### Set variables for sending logs:\n\n```\nMONITORING_TOOL__SERVICE_ENV=$CI_ENVIRONMENT_NAME\n```\n\n```\nMONITORING_TOOL__SERVICE_NAME=greantess-funnel\n```\n\n```\nMONITORING_TOOL__SERVICE_VERSION=$CI_COMMIT_SHA\n```\n\n```\nMONITORING_TOOL__API_KEY=4be_your_api_key\n```\n\n### Usage\n\n#### Approach with facade\n\n`initServerMonitoring` is a facade over `ServerMonitoringService`.\nYou can instantiate and use that service directly.\nThis function basically does one thing - instantiate it and can do 3 more additional things:\n\n- call `overrideNativeConsole` - method of the service to override native console, to log to datadog instead.\n- cal `catchProcessErrors` - method of the service to subscribe to native errors, to log them to datadog.\n- put service itself into global scope under defined name, so serverside code can use it.\n\nYou may wonder why we instantiate service here and not in server-side code.\nThe reason for that is if we override the native console and catch native errors - we would like to set up this as soon as possible.\nIf you don’t care too much about the very first seconds of next server - you can use alternative simpler server side logging solution.\n\nExample(for NextJS):\n\n- update next.config.ts to include into start script of production server code\n  `next.config.ts:`\n\n```\nconst { initServerMonitoring } = require('@kilohealth/web-app-monitoring/dist/server');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    const remoteMonitoringServiceParams = {\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    };\n    config = {\n      shouldOverrideNativeConsole: true,\n      shouldCatchProcessErrors: true,\n      globalMonitoringInstanceName: 'kiloServerMonitoring',\n    }\n    initServerMonitoring(remoteMonitoringServiceParams, config);\n  }\n}\n```\n\n- update `custom.d.ts` file to declare that global scope now have monitoring service as a prop\n  In order to use ServerMonitoringService instance in other parts of code via global we need to let TS know\n  that we added new property to global object.\n  In NextJS you can just create or add next code into `custom.d.ts` file in root of the project.\n  Be aware that var name matches string that you provided in code above (kiloServerMonitoring in this case).\n  `custom.d.ts:`\n\n```\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\ndeclare global {\n  // eslint-disable-next-line no-var\n  var kiloServerMonitoring: ServerMonitoringService;\n}\n```\n\n- use it in code\n\n```\nexport const getHomeServerSideProps = async context => {\n  global.kiloServerMonitoring.info('getHomeServerSideProps called');\n  ...\n};\n```\n\n#### Approach with direct instantiation\n\nIf you don’t care too much about catching native errors or native logs\nin the early stages of your server app -\nyou can avoid sharing logger via global scope and instead initialize it inside of app.\n\n```\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\nexport const monitoring = new ServerMonitoringService({\n  authToken: MONITORING_TOOL__API_KEY,\n  serviceName: MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: MONITORING_TOOL__SERVICE_ENV,\n})\n```\n\nAs you can see we are using here all our env variables.\nIf any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n### Tracing setup:\n\nWe need to connect tracing as soon as possible during code, so it can be injected into all base modules for APM monitoring.\nTracing module is available via:\n\n```\nconst { initTracing } = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\ninitTracing({\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n});\n```\n\nExample(NextJS):\n\n```\nconst { PHASE_PRODUCTION_SERVER } = require('next/constants');\nconst { initTracing } = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    initTracing({\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    });\n  }\n}\n```\n\n> In newer versions of NextJS there is experimental feature called\n> [instrumentationHook](https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation)\n> We can opt out from using undocumented `PHASE_PRODUCTION_SERVER` to use `instrumentationHook` for tracing init.\n\n> There is also possibility to use `NODE_OPTIONS='-r ./prestart-script.js ' next start` instead.\n> But there is an issue with `pino-datadog-transport`, which for performance reason spawns separate thread for log sending to data-dog and it this option seems to be passed to that process as well which triggers an infinite loop of require and initialization.\n\n## API\n\n### MonitoringService (both BrowserMonitoringService and ServerMonitoringService have these methods)\n\n#### debug, info, warn\n\n```\ndebug(message: string, context?: object)\ninfo(message: string, context?: object)\nwarn(message: string, context?: object)\n```\n\n- `message` - any message to be logged\n- `context` - object with all needed and related to the log entrance data\n\n#### error\n\n```\nerror(message: string, context?: object, error?: Error)\n```\n\nSame as above, but you can also optionally pass error instance as thrid parameter\n\n#### reportError\n\n```\nreportError(error: Error, context?: object)\n```\n\nShortcut for `service.error()`, which uses `error.message` field as message param for `error` method.\n\n### BrowserMonitoringService\n\n#### constructor\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```\ninterface RemoteMonitoringServiceParams {\n  serviceName?: string;\n  serviceVersion?: string;\n  serviceEnv?: string;\n  authToken?: string;\n}\n```\n\n- `RemoteMonitoringServiceConfig` - datadog params passed to init function. More info in [docs](https://docs.datadoghq.com/logs/log_collection/javascript/#initialization-parameters)\n- `serviceName` - name of the service\n- `serviceVersion` - version of the service\n- `serviceEnv` - environment where service is deployed\n- `authToken` - client token\n\n### ServerMonitoringService\n\n#### constructor\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```\ninterface RemoteMonitoringServiceConfig {\n  transportOptions?: Partial<TransportBaseOptions>;\n  loggerOptions?: Partial<LoggerOptions>;\n}\n```\n\n- `transportOptions` - [pino-datadog-transport options](https://github.com/theogravity/pino-datadog-transport#configuration-options)\n- `loggerOptions` - [pino logger options](https://getpino.io/#/docs/api?id=options-object)\n\n#### overrideLogger\n\nOverrides logger passed as argument with monitoring logger.\nAll methods of this logger will be overridden with corresponding methods of server monitoring.\n\n```\noverrideLogger(unknownLogger: UnknownLogger)\n```\n\n```\ninterface UnknownLogger {\n  log?(...parts: unknown[]): void;\n  debug?(...parts: unknown[]): void;\n  info(...parts: unknown[]): void;\n  warn(...parts: unknown[]): void;\n  error(...parts: unknown[]): void;\n}\n```\n\n#### overrideNativeConsole\n\nCalls overrideLogger for native console.\n\n```\noverrideNativeConsole()\n```\n\n#### catchProcessErrors\n\nSubscribes to `unhandledRejection` and `uncaughtException` events of the process to report `error` in such cases.\n\n```\ncatchProcessErrors()\n```\n\n### ServerMonitoringService\n\n#### initServerMonitoring\n\nInstantiate ServerMonitoringService with provided params and may also do additional work,\ndepending on provided variables.\n\n```\ninitServerMonitoring = (\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  monitoringOptions?: MonitoringOptions,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig\n): ServerMonitoringService\n```\n\n```\ninterface MonitoringOptions {\n  shouldOverrideNativeConsole?: boolean;\n  shouldCatchProcessErrors?: boolean;\n  globalMonitoringInstanceName?: string;\n}\n```\n\n- `RemoteMonitoringServiceParams` and `RemoteMonitoringServiceConfig` are same as in `constructor` api\n- `shouldOverrideNativeConsole` - if `true`, will call `serverMonitoringService.overrideNativeConsole()` under the hood\n- `shouldCatchProcessErrors` - if `true`, will call `serverMonitoringService.catchProcessErrors()` under the hood\n- `globalMonitoringInstanceName` - if provided with non-empty string will put instantiated `serverMonitoringService` into global scope under provided name.\n","readmeFilename":"README.md","gitHead":"30e837600165be417d01e1c5725f877773c78820","description":"[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)","_id":"@kilohealth/web-app-monitoring@1.0.0-alpha.14","_nodeVersion":"18.16.0","_npmVersion":"9.6.7","dist":{"integrity":"sha512-e59b/rsAxFTlDaDWdmxyapaG8u1UKroI38200F2CK30DRxMKoK2EMWru60fQNzTYPYcWNfZS3ePoRBfqN6uOfQ==","shasum":"bc0737d866d09cc4254827e58af89d1800f2c89f","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.0.0-alpha.14.tgz","fileCount":40,"unpackedSize":52921,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGfqs2ow+RdP4tq+XIw+zxynJUXEpGKUwIPxYgg/CugKAiEAw0bZBPeBW7mh7s9Owj+/R4vlVx2zj+LGD35tqBWk7eM="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.0.0-alpha.14_1686898402499_0.9786586331520148"},"_hasShrinkwrap":false},"1.0.0-alpha.15":{"name":"@kilohealth/web-app-monitoring","version":"1.0.0-alpha.15","license":"MIT","author":{"name":"Kilo Health"},"exports":{"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","prepare":"husky install","semantic-release":"semantic-release","test":"jest","upload:sourcemaps":"./dist/cli/sourcemap-uploader.sh"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","@datadog/datadog-ci":"^2.11.0","dd-trace":"^4.0.0","deepmerge":"^4.3.1","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","@types/lodash":"^4.14.195","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jest-environment-jsdom":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"readme":"# @kilohealth/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\n## The Idea\n\nPackage was created to abstract away underlying level of monitoring,\nto make it easy to setup monitoring for any env as well as to make it easy to migrate from DataDog to other monitoring solution.\n\nThe package consists of 3 parts:\n\n- browser monitoring\n- CLI (needed to upload sourcemaps for browser monitoring)\n- server monitoring\n\n## Browser monitoring setup:\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n> If you migrating from direct datadog integration - don’t forget to remove @datadog/browser-logs and @datadog/datadog-ci. Those are now deps of @kilohealth/web-app-monitoring.\n\n```\nnpm uninstall @datadog/browser-logs @datadog/datadog-ci\n```\n\n### Add build phase env variables\n\n- `MONITORING_TOOL__API_KEY` - this key is needed in order to uploaded sourcemaps for browser monitoring. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048).\n- `MONITORING_TOOL__SERVICE_NAME`, `MONITORING_TOOL__SERVICE_VERSION` and `MONITORING_TOOL__SERVICE_ENV`- the service name, version and env. These variables will be used by client code as well as by cli. Most client frameworks will not expose all node build phase env vars, so you probably need to reexpose them with prefix to switch on automatic replacement for client code during client build. In particular\n  - For NextJS - you have to add prefix `NEXT_PUBLIC_` to each of them. For example you have to add not only `MONITORING_TOOL__SERVICE_NAME=timely-hand-web-funnel-app` but also `NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME`. See more in [docs](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#exposing-environment-variables-to-the-browser).\n  - For GatsbyJS - you have to add prefix `GATSBY_`. See more in [docs](https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/#accessing-environment-variables-in-the-browser)\n  - For ViteJS - you have to add prefix `VITE_`. See more in [docs](https://vitejs.dev/guide/env-and-mode.html#env-files).\n- `MONITORING_TOOL__CLIENT_TOKEN` - this is client side token, which need to be built into client code in order to send logs into DD server.\n  Because it is needed on client you will have to re-expose it using same approach as variables above (probably prefixing env var).\n  Token you can create or find [here](https://app.datadoghq.com/organization-settings/client-tokens).\n  > PS: theoretically you can avoid creating `MONITORING_TOOL__CLIENT_TOKEN` env variable and create only `NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN` instead, because this variable is not needed for CLI to work. But for the sake of SRP we advocate for sticking with same reexposing approach here.\n\n#### Example of full env variables setup for client monitoring:\n\n##### Expose client token to be able to reexpose it for client-side code:\n\n```\nMONITORING_TOOL__CLIENT_TOKEN=pub2_your_client_token\n```\n\n##### Set variables for source map upload CLI to work during the build phase:\n\n```\nMONITORING_TOOL__SERVICE_ENV=$CI_ENVIRONMENT_NAME\n```\n\n```\nMONITORING_TOOL__SERVICE_NAME=greantess-funnel\n```\n\n```\nMONITORING_TOOL__SERVICE_VERSION=$CI_COMMIT_SHA\n```\n\n```\nMONITORING_TOOL__API_KEY=4be_your_api_key\n```\n\n##### Reexpose for your framework to client-side code(NextJS example):\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV=$MONITORING_TOOL__SERVICE_ENV\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION=$MONITORING_TOOL__SERVICE_VERSION\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN=$MONITORING_TOOL__CLIENT_TOKEN\n```\n\n### Modify your build code to generate sourcemaps, depending on env variable\n\nIdeally we don't want to generate and upload sourcemaps during each build.\nIn order to opt-in for this behavior sometimes we need to make additional configuration changes in our build process.\nWe need to build sourcemaps only in case specific env variable `IS_SOURCEMAP_UPLOAD_BUILD` is provided.\nWe don't provide it for dev or debug builds, only for production.\nThese are articles on how to do this for different frameworks and examples.\n\n- [Vite](https://vitejs.dev/config/build-options.html#build-sourcemap)\n\n```\nexport default defineConfig({\n  ...\n  build: {\n    sourcemap: Boolean(process.env.IS_SOURCEMAP_UPLOAD_BUILD),\n    ...\n  }\n  ...\n})\n```\n\n- [NextJS](https://nextjs.org/docs/pages/api-reference/next-config-js/productionBrowserSourceMaps)\n\n```\nmodule.exports = {\n  ...\n  productionBrowserSourceMaps: Boolean(process.env.IS_SOURCEMAP_UPLOAD_BUILD),\n  ...\n}\n```\n\n- Gatsby is a little bit more tricky. It generates sourcemaps by default. In order to prevent this you can add code to\n\n```\nmodule.exports.onCreateWebpackConfig = ({ stage, actions }) => {\n  // build-javascript is prod build phase\n  if (stage === 'build-javascript') {\n    actions.setWebpackConfig({\n      // hidden-source-map removes last line from final files,\n      // to avoid contenthash mismatch between builds\n      // we don't want sourcemaps in prod by default\n      devtool: process.env.IS_SOURCEMAP_UPLOAD_BUILD\n        ? 'hidden-source-map'\n        : false,\n    });\n  }\n};\n```\n\nThere is also an [article](https://akashrajpurohit.com/blog/disable-source-maps-in-gatsbyjs-v2/) with more details.\n\n### Add build and upload sourcemaps script to scripts section\n\n#### Prepare sourcemap upload build\n\nIt should run bin from our lib called `web-app-monitoring__upload-sourcemaps`.\nFor this script to work you would need to provide it with two variables\n\n- `MONITORING_TOOL__PUBLIC_PATH` - this is RELATIVE path, part of URL between domain (which can be different for different environments) and path to file itself.\n  In other words - base path for all the assets within your application.\n  You can think of this as kind of relative [Public Path | webpack](https://webpack.js.org/guides/public-path/).\n  For example it can be `/` or `/static`.\n  In other words this is common relative prefix for all your static files or / if there is none.\n  - for Vite default is `/`\n  - for NextJS default is `/_next/static/chunks` (!!! `_` instead of `.` in file system)\n  - for GatsbyJS default is `/`\n- `MONITORING_TOOL__BUILD_DIR` - this should be RELATIVE path to your build directory. For example `./dist` or `./build`.\n  - for Vite default is `./dist`\n  - for NextJS default is `./.next/static/chunks`\n  - for GatsbyJS default is `./public`\n\nExample for NextJS\n\n```\nMONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\n```\n\n#### 1. Approach with parallel build\n\nWe advocate for this approach.\nWith it you have separate script to build code for deployment and another one to make a build with sourcemaps to upload those to monitoring tool.\nWe decided to extract source map building and uploading into separate step because:\n\n- not each build may need these, and it will increase build time. For example, you may want to avoid this for dev builds.\n- we don’t want to manually alter the build (aka removing sourcemaps from it) because it is fragile and hard to maintain\n- we don’t want sourcemaps to leak into production, so we wanna separate generating them and uploading files into prod into different processes\n\nExample for NextJS\n\n```\n\"upload:sourcemaps\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 npm run build && MONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\"\n```\n\nAnd then your CI should run `upload:sourcemaps` script in parallel with main build,\nto avoid blocking and increasing main build time.\n\n#### 2. Approach with same build\n\nThere is alternative approach to tweak main build process with sourcemaps.\nWe need to do next things:\n\n- switch sourcemaps to be hidden.\n  For example instead of using option sourcemaps use hidden-sourcemaps.\n  With this we will avoid warning in console in production regarding the fact that files have a reference to sourcemaps but sourcemaps are not found.\n  We are just removing this reference during build phase.\n\n- extend usual build script with flag to include sourcemaps `IS_SOURCEMAP_UPLOAD_BUILD`,\n  script to upload them and script to remove them. For example\n\n```\n\"only-upload:sourcemaps\": \"MONITORING_TOOL__BUILD_DIR=./public MONITORING_TOOL__PUBLIC_PATH=/ web-app-monitoring__upload-sourcemaps\",\n\"remove:sourcemaps\": \"find ./public -name \\\"*.map\\\" -type f -delete\",\n\"build\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 gatsby build --prefix-paths && npm run only-upload:sourcemaps && npm run remove:sourcemaps\",\n```\n\n### Usage: Import and instantiate BrowserMonitoringService\n\n> Important note: there is no single entry point for package. You can't do smth like `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring';`\n> Reason for that is to avoid bundling server-code into client bundle and vice versa. This structure will ensure effective tree shaking during build time.\n\n```\nimport { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/browser';\n\nexport const monitoring = new BrowserMonitoringService({\n  authToken: NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN,\n  serviceName: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV,\n})\n```\n\nAs you can see we are using here all our exposed variables. If any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n#### OPTIONAL: If you are using React you may benefit from utilizing [Error Boundaries](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary):\n\n```\nimport React, { Component, PropsWithChildren } from 'react';\nimport { monitoring } from '../services/monitoring';\n\ninterface ErrorBoundaryProps {}\ninterface ErrorBoundaryState {\n  hasError: boolean;\n}\n\nexport class ErrorBoundary extends Component<\n  PropsWithChildren<ErrorBoundaryProps>,\n  ErrorBoundaryState\n> {\n  static getDerivedStateFromError(_: Error): ErrorBoundaryState {\n    return { hasError: true };\n  }\n\n  state: ErrorBoundaryState = {\n    hasError: false,\n  };\n\n  componentDidCatch(error: Error) {\n    monitoring.reportError(error);\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return <h1>Sorry... There was an error</h1>;\n    }\n\n    return this.props.children;\n  }\n}\n```\n\n## Server monitoring setup (NextJS):\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n### Add build phase env variables\n\n- `MONITORING_TOOL__API_KEY` - this key is needed in order to send logs. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048).\n- `MONITORING_TOOL__SERVICE_NAME`, `MONITORING_TOOL__SERVICE_VERSION` and `MONITORING_TOOL__SERVICE_ENV` - the service name, version and env. These variables will be used to send logs.\n\n#### Example of full env variables setup for server monitoring:\n\n##### Set variables for sending logs:\n\n```\nMONITORING_TOOL__SERVICE_ENV=$CI_ENVIRONMENT_NAME\n```\n\n```\nMONITORING_TOOL__SERVICE_NAME=greantess-funnel\n```\n\n```\nMONITORING_TOOL__SERVICE_VERSION=$CI_COMMIT_SHA\n```\n\n```\nMONITORING_TOOL__API_KEY=4be_your_api_key\n```\n\n### Usage\n\n#### Approach with facade\n\n`initServerMonitoring` is a facade over `ServerMonitoringService`.\nYou can instantiate and use that service directly.\nThis function basically does one thing - instantiate it and can do 3 more additional things:\n\n- call `overrideNativeConsole` - method of the service to override native console, to log to datadog instead.\n- cal `catchProcessErrors` - method of the service to subscribe to native errors, to log them to datadog.\n- put service itself into global scope under defined name, so serverside code can use it.\n\nYou may wonder why we instantiate service here and not in server-side code.\nThe reason for that is if we override the native console and catch native errors - we would like to set up this as soon as possible.\nIf you don’t care too much about the very first seconds of next server - you can use alternative simpler server side logging solution.\n\nExample(for NextJS):\n\n- update next.config.ts to include into start script of production server code\n  `next.config.ts:`\n\n```\nconst { initServerMonitoring } = require('@kilohealth/web-app-monitoring/server');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    const remoteMonitoringServiceParams = {\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    };\n    const config = {\n      shouldOverrideNativeConsole: true,\n      shouldCatchProcessErrors: true,\n      globalMonitoringInstanceName: 'kiloServerMonitoring',\n    }\n    initServerMonitoring(remoteMonitoringServiceParams, config);\n  }\n}\n```\n\n- update `custom.d.ts` file to declare that global scope now have monitoring service as a prop\n  In order to use ServerMonitoringService instance in other parts of code via global we need to let TS know\n  that we added new property to global object.\n  In NextJS you can just create or add next code into `custom.d.ts` file in root of the project.\n  Be aware that var name matches string that you provided in code above (kiloServerMonitoring in this case).\n  `custom.d.ts:`\n\n```\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/server';\n\ndeclare global {\n  // eslint-disable-next-line no-var\n  var kiloServerMonitoring: ServerMonitoringService;\n}\n```\n\n- use it in code\n\n```\nexport const getHomeServerSideProps = async context => {\n  global.kiloServerMonitoring.info('getHomeServerSideProps called');\n  ...\n};\n```\n\n#### Approach with direct instantiation\n\nIf you don’t care too much about catching native errors or native logs\nin the early stages of your server app -\nyou can avoid sharing logger via global scope and instead initialize it inside of app.\n\n```\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/server';\n\nexport const monitoring = new ServerMonitoringService({\n  authToken: MONITORING_TOOL__API_KEY,\n  serviceName: MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: MONITORING_TOOL__SERVICE_ENV,\n})\n```\n\nAs you can see we are using here all our env variables.\nIf any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n### Tracing setup:\n\nWe need to connect tracing as soon as possible during code, so it can be injected into all base modules for APM monitoring.\nTracing module is available via:\n\n```\nconst { initTracing } = require('@kilohealth/web-app-monitoring/initTracing');\ninitTracing({\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n});\n```\n\nExample(NextJS):\n\n```\nconst { PHASE_PRODUCTION_SERVER } = require('next/constants');\nconst { initTracing } = require('@kilohealth/web-app-monitoring/initTracing');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    initTracing({\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    });\n  }\n}\n```\n\n> In newer versions of NextJS there is experimental feature called\n> [instrumentationHook](https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation)\n> We can opt out from using undocumented `PHASE_PRODUCTION_SERVER` to use `instrumentationHook` for tracing init.\n\n> There is also possibility to use `NODE_OPTIONS='-r ./prestart-script.js ' next start` instead.\n> But there is an issue with `pino-datadog-transport`, which for performance reason spawns separate thread for log sending to data-dog and it this option seems to be passed to that process as well which triggers an infinite loop of require and initialization.\n\n## API\n\n### MonitoringService (both BrowserMonitoringService and ServerMonitoringService have these methods)\n\n#### debug, info, warn\n\n```\ndebug(message: string, context?: object)\ninfo(message: string, context?: object)\nwarn(message: string, context?: object)\n```\n\n- `message` - any message to be logged\n- `context` - object with all needed and related to the log entrance data\n\n#### error\n\n```\nerror(message: string, context?: object, error?: Error)\n```\n\nSame as above, but you can also optionally pass error instance as thrid parameter\n\n#### reportError\n\n```\nreportError(error: Error, context?: object)\n```\n\nShortcut for `service.error()`, which uses `error.message` field as message param for `error` method.\n\n### BrowserMonitoringService\n\n#### constructor\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```\ninterface RemoteMonitoringServiceParams {\n  serviceName?: string;\n  serviceVersion?: string;\n  serviceEnv?: string;\n  authToken?: string;\n}\n```\n\n- `RemoteMonitoringServiceConfig` - datadog params passed to init function. More info in [docs](https://docs.datadoghq.com/logs/log_collection/javascript/#initialization-parameters)\n- `serviceName` - name of the service\n- `serviceVersion` - version of the service\n- `serviceEnv` - environment where service is deployed\n- `authToken` - client token\n\n### ServerMonitoringService\n\n#### constructor\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```\ninterface RemoteMonitoringServiceConfig {\n  transportOptions?: Partial<TransportBaseOptions>;\n  loggerOptions?: Partial<LoggerOptions>;\n}\n```\n\n- `transportOptions` - [pino-datadog-transport options](https://github.com/theogravity/pino-datadog-transport#configuration-options)\n- `loggerOptions` - [pino logger options](https://getpino.io/#/docs/api?id=options-object)\n\n#### overrideLogger\n\nOverrides logger passed as argument with monitoring logger.\nAll methods of this logger will be overridden with corresponding methods of server monitoring.\n\n```\noverrideLogger(unknownLogger: UnknownLogger)\n```\n\n```\ninterface UnknownLogger {\n  log?(...parts: unknown[]): void;\n  debug?(...parts: unknown[]): void;\n  info(...parts: unknown[]): void;\n  warn(...parts: unknown[]): void;\n  error(...parts: unknown[]): void;\n}\n```\n\n#### overrideNativeConsole\n\nCalls overrideLogger for native console.\n\n```\noverrideNativeConsole()\n```\n\n#### catchProcessErrors\n\nSubscribes to `unhandledRejection` and `uncaughtException` events of the process to report `error` in such cases.\n\n```\ncatchProcessErrors()\n```\n\n### ServerMonitoringService\n\n#### initServerMonitoring\n\nInstantiate ServerMonitoringService with provided params and may also do additional work,\ndepending on provided variables.\n\n```\ninitServerMonitoring = (\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  monitoringOptions?: MonitoringOptions,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig\n): ServerMonitoringService\n```\n\n```\ninterface MonitoringOptions {\n  shouldOverrideNativeConsole?: boolean;\n  shouldCatchProcessErrors?: boolean;\n  globalMonitoringInstanceName?: string;\n}\n```\n\n- `RemoteMonitoringServiceParams` and `RemoteMonitoringServiceConfig` are same as in `constructor` api\n- `shouldOverrideNativeConsole` - if `true`, will call `serverMonitoringService.overrideNativeConsole()` under the hood\n- `shouldCatchProcessErrors` - if `true`, will call `serverMonitoringService.catchProcessErrors()` under the hood\n- `globalMonitoringInstanceName` - if provided with non-empty string will put instantiated `serverMonitoringService` into global scope under provided name.\n","readmeFilename":"README.md","gitHead":"8324cb0da2a0f47201cc5a865986cbdcd9be1ddf","description":"[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)","_id":"@kilohealth/web-app-monitoring@1.0.0-alpha.15","_nodeVersion":"18.16.0","_npmVersion":"9.6.7","dist":{"integrity":"sha512-cy2V7wf0DDyNxDtGjGBRhkACYWoaYrxulhRq+bnToEO2pB3Tf3FmhgIh5P8vvazV1YV7z+ZjeWk7+aM4d+87iw==","shasum":"8993a7bdc50f1272c2a953d5a521996520a74d84","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.0.0-alpha.15.tgz","fileCount":40,"unpackedSize":52920,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCwUh3uiJxhZRQyrSrgpZkKYX0PG5bifV7ZAFuMNv1cVQIgZrBeKEoPs0DKi+/+KJO6R0Es9dQy6deoDwxGGI1dnj0="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.0.0-alpha.15_1687315515338_0.08237281206130209"},"_hasShrinkwrap":false},"1.0.0-alpha.16":{"name":"@kilohealth/web-app-monitoring","version":"1.0.0-alpha.16","license":"MIT","author":{"name":"Kilo Health"},"exports":{"./dist/server/initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./dist/server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./dist/browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"main":"./dist/server/index.js","browser":"./dist/browser/index.js","types":"./dist/server/index.d.ts","bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","githooks:commit-msg":"commitlint --edit","githooks:pre-commit":"lint-staged","prepare":"husky install","semantic-release":"semantic-release","test":"jest","upload:sourcemaps":"./dist/cli/sourcemap-uploader.sh"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","@datadog/datadog-ci":"^2.11.0","dd-trace":"^4.0.0","deepmerge":"^4.3.1","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","@types/lodash":"^4.14.195","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jest-environment-jsdom":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"readme":"# @kilohealth/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\n## The Idea\n\nPackage was created to abstract away underlying level of monitoring,\nto make it easy to setup monitoring for any env as well as to make it easy to migrate from DataDog to other monitoring solution.\n\nThe package consists of 3 parts:\n\n- browser monitoring\n- CLI (needed to upload sourcemaps for browser monitoring)\n- server monitoring\n\n## Browser monitoring setup:\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n> If you migrating from direct datadog integration - don’t forget to remove @datadog/browser-logs and @datadog/datadog-ci. Those are now deps of @kilohealth/web-app-monitoring.\n\n```\nnpm uninstall @datadog/browser-logs @datadog/datadog-ci\n```\n\n### Add build phase env variables\n\n- `MONITORING_TOOL__API_KEY` - this key is needed in order to uploaded sourcemaps for browser monitoring. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048).\n- `MONITORING_TOOL__SERVICE_NAME`, `MONITORING_TOOL__SERVICE_VERSION` and `MONITORING_TOOL__SERVICE_ENV`- the service name, version and env. These variables will be used by client code as well as by cli. Most client frameworks will not expose all node build phase env vars, so you probably need to reexpose them with prefix to switch on automatic replacement for client code during client build. In particular\n  - For NextJS - you have to add prefix `NEXT_PUBLIC_` to each of them. For example you have to add not only `MONITORING_TOOL__SERVICE_NAME=timely-hand-web-funnel-app` but also `NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME`. See more in [docs](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#exposing-environment-variables-to-the-browser).\n  - For GatsbyJS - you have to add prefix `GATSBY_`. See more in [docs](https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/#accessing-environment-variables-in-the-browser)\n  - For ViteJS - you have to add prefix `VITE_`. See more in [docs](https://vitejs.dev/guide/env-and-mode.html#env-files).\n- `MONITORING_TOOL__CLIENT_TOKEN` - this is client side token, which need to be built into client code in order to send logs into DD server.\n  Because it is needed on client you will have to re-expose it using same approach as variables above (probably prefixing env var).\n  Token you can create or find [here](https://app.datadoghq.com/organization-settings/client-tokens).\n  > PS: theoretically you can avoid creating `MONITORING_TOOL__CLIENT_TOKEN` env variable and create only `NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN` instead, because this variable is not needed for CLI to work. But for the sake of SRP we advocate for sticking with same reexposing approach here.\n\n#### Example of full env variables setup for client monitoring:\n\n##### Expose client token to be able to reexpose it for client-side code:\n\n```\nMONITORING_TOOL__CLIENT_TOKEN=pub2_your_client_token\n```\n\n##### Set variables for source map upload CLI to work during the build phase:\n\n```\nMONITORING_TOOL__SERVICE_ENV=$CI_ENVIRONMENT_NAME\n```\n\n```\nMONITORING_TOOL__SERVICE_NAME=greantess-funnel\n```\n\n```\nMONITORING_TOOL__SERVICE_VERSION=$CI_COMMIT_SHA\n```\n\n```\nMONITORING_TOOL__API_KEY=4be_your_api_key\n```\n\n##### Reexpose for your framework to client-side code(NextJS example):\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV=$MONITORING_TOOL__SERVICE_ENV\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION=$MONITORING_TOOL__SERVICE_VERSION\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN=$MONITORING_TOOL__CLIENT_TOKEN\n```\n\n### Modify your build code to generate sourcemaps, depending on env variable\n\nIdeally we don't want to generate and upload sourcemaps during each build.\nIn order to opt-in for this behavior sometimes we need to make additional configuration changes in our build process.\nWe need to build sourcemaps only in case specific env variable `IS_SOURCEMAP_UPLOAD_BUILD` is provided.\nWe don't provide it for dev or debug builds, only for production.\nThese are articles on how to do this for different frameworks and examples.\n\n- [Vite](https://vitejs.dev/config/build-options.html#build-sourcemap)\n\n```\nexport default defineConfig({\n  ...\n  build: {\n    sourcemap: Boolean(process.env.IS_SOURCEMAP_UPLOAD_BUILD),\n    ...\n  }\n  ...\n})\n```\n\n- [NextJS](https://nextjs.org/docs/pages/api-reference/next-config-js/productionBrowserSourceMaps)\n\n```\nmodule.exports = {\n  ...\n  productionBrowserSourceMaps: Boolean(process.env.IS_SOURCEMAP_UPLOAD_BUILD),\n  ...\n}\n```\n\n- Gatsby is a little bit more tricky. It generates sourcemaps by default. In order to prevent this you can add code to\n\n```\nmodule.exports.onCreateWebpackConfig = ({ stage, actions }) => {\n  // build-javascript is prod build phase\n  if (stage === 'build-javascript') {\n    actions.setWebpackConfig({\n      // hidden-source-map removes last line from final files,\n      // to avoid contenthash mismatch between builds\n      // we don't want sourcemaps in prod by default\n      devtool: process.env.IS_SOURCEMAP_UPLOAD_BUILD\n        ? 'hidden-source-map'\n        : false,\n    });\n  }\n};\n```\n\nThere is also an [article](https://akashrajpurohit.com/blog/disable-source-maps-in-gatsbyjs-v2/) with more details.\n\n### Add build and upload sourcemaps script to scripts section\n\n#### Prepare sourcemap upload build\n\nIt should run bin from our lib called `web-app-monitoring__upload-sourcemaps`.\nFor this script to work you would need to provide it with two variables\n\n- `MONITORING_TOOL__PUBLIC_PATH` - this is RELATIVE path, part of URL between domain (which can be different for different environments) and path to file itself.\n  In other words - base path for all the assets within your application.\n  You can think of this as kind of relative [Public Path | webpack](https://webpack.js.org/guides/public-path/).\n  For example it can be `/` or `/static`.\n  In other words this is common relative prefix for all your static files or / if there is none.\n  - for Vite default is `/`\n  - for NextJS default is `/_next/static/chunks` (!!! `_` instead of `.` in file system)\n  - for GatsbyJS default is `/`\n- `MONITORING_TOOL__BUILD_DIR` - this should be RELATIVE path to your build directory. For example `./dist` or `./build`.\n  - for Vite default is `./dist`\n  - for NextJS default is `./.next/static/chunks`\n  - for GatsbyJS default is `./public`\n\nExample for NextJS\n\n```\nMONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\n```\n\n#### 1. Approach with parallel build\n\nWe advocate for this approach.\nWith it you have separate script to build code for deployment and another one to make a build with sourcemaps to upload those to monitoring tool.\nWe decided to extract source map building and uploading into separate step because:\n\n- not each build may need these, and it will increase build time. For example, you may want to avoid this for dev builds.\n- we don’t want to manually alter the build (aka removing sourcemaps from it) because it is fragile and hard to maintain\n- we don’t want sourcemaps to leak into production, so we wanna separate generating them and uploading files into prod into different processes\n\nExample for NextJS\n\n```\n\"upload:sourcemaps\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 npm run build && MONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\"\n```\n\nAnd then your CI should run `upload:sourcemaps` script in parallel with main build,\nto avoid blocking and increasing main build time.\n\n#### 2. Approach with same build\n\nThere is alternative approach to tweak main build process with sourcemaps.\nWe need to do next things:\n\n- switch sourcemaps to be hidden.\n  For example instead of using option sourcemaps use hidden-sourcemaps.\n  With this we will avoid warning in console in production regarding the fact that files have a reference to sourcemaps but sourcemaps are not found.\n  We are just removing this reference during build phase.\n\n- extend usual build script with flag to include sourcemaps `IS_SOURCEMAP_UPLOAD_BUILD`,\n  script to upload them and script to remove them. For example\n\n```\n\"only-upload:sourcemaps\": \"MONITORING_TOOL__BUILD_DIR=./public MONITORING_TOOL__PUBLIC_PATH=/ web-app-monitoring__upload-sourcemaps\",\n\"remove:sourcemaps\": \"find ./public -name \\\"*.map\\\" -type f -delete\",\n\"build\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 gatsby build --prefix-paths && npm run only-upload:sourcemaps && npm run remove:sourcemaps\",\n```\n\n### Usage: Import and instantiate BrowserMonitoringService\n\n> Important note: there is no single entry point for package. You can't do smth like `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring';`\n> Reason for that is to avoid bundling server-code into client bundle and vice versa. This structure will ensure effective tree shaking during build time.\n\n```\nimport { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/browser';\n\nexport const monitoring = new BrowserMonitoringService({\n  authToken: NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN,\n  serviceName: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV,\n})\n```\n\nAs you can see we are using here all our exposed variables. If any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n#### OPTIONAL: If you are using React you may benefit from utilizing [Error Boundaries](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary):\n\n```\nimport React, { Component, PropsWithChildren } from 'react';\nimport { monitoring } from '../services/monitoring';\n\ninterface ErrorBoundaryProps {}\ninterface ErrorBoundaryState {\n  hasError: boolean;\n}\n\nexport class ErrorBoundary extends Component<\n  PropsWithChildren<ErrorBoundaryProps>,\n  ErrorBoundaryState\n> {\n  static getDerivedStateFromError(_: Error): ErrorBoundaryState {\n    return { hasError: true };\n  }\n\n  state: ErrorBoundaryState = {\n    hasError: false,\n  };\n\n  componentDidCatch(error: Error) {\n    monitoring.reportError(error);\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return <h1>Sorry... There was an error</h1>;\n    }\n\n    return this.props.children;\n  }\n}\n```\n\n## Server monitoring setup (NextJS):\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n### Add build phase env variables\n\n- `MONITORING_TOOL__API_KEY` - this key is needed in order to send logs. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048).\n- `MONITORING_TOOL__SERVICE_NAME`, `MONITORING_TOOL__SERVICE_VERSION` and `MONITORING_TOOL__SERVICE_ENV` - the service name, version and env. These variables will be used to send logs.\n\n#### Example of full env variables setup for server monitoring:\n\n##### Set variables for sending logs:\n\n```\nMONITORING_TOOL__SERVICE_ENV=$CI_ENVIRONMENT_NAME\n```\n\n```\nMONITORING_TOOL__SERVICE_NAME=greantess-funnel\n```\n\n```\nMONITORING_TOOL__SERVICE_VERSION=$CI_COMMIT_SHA\n```\n\n```\nMONITORING_TOOL__API_KEY=4be_your_api_key\n```\n\n### Usage\n\n#### Approach with facade\n\n`initServerMonitoring` is a facade over `ServerMonitoringService`.\nYou can instantiate and use that service directly.\nThis function basically does one thing - instantiate it and can do 3 more additional things:\n\n- call `overrideNativeConsole` - method of the service to override native console, to log to datadog instead.\n- cal `catchProcessErrors` - method of the service to subscribe to native errors, to log them to datadog.\n- put service itself into global scope under defined name, so serverside code can use it.\n\nYou may wonder why we instantiate service here and not in server-side code.\nThe reason for that is if we override the native console and catch native errors - we would like to set up this as soon as possible.\nIf you don’t care too much about the very first seconds of next server - you can use alternative simpler server side logging solution.\n\nExample(for NextJS):\n\n- update next.config.ts to include into start script of production server code\n  `next.config.ts:`\n\n```\nconst { initServerMonitoring } = require('@kilohealth/web-app-monitoring/server');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    const remoteMonitoringServiceParams = {\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    };\n    const config = {\n      shouldOverrideNativeConsole: true,\n      shouldCatchProcessErrors: true,\n      globalMonitoringInstanceName: 'kiloServerMonitoring',\n    }\n    initServerMonitoring(remoteMonitoringServiceParams, config);\n  }\n}\n```\n\n- update `custom.d.ts` file to declare that global scope now have monitoring service as a prop\n  In order to use ServerMonitoringService instance in other parts of code via global we need to let TS know\n  that we added new property to global object.\n  In NextJS you can just create or add next code into `custom.d.ts` file in root of the project.\n  Be aware that var name matches string that you provided in code above (kiloServerMonitoring in this case).\n  `custom.d.ts:`\n\n```\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/server';\n\ndeclare global {\n  // eslint-disable-next-line no-var\n  var kiloServerMonitoring: ServerMonitoringService;\n}\n```\n\n- use it in code\n\n```\nexport const getHomeServerSideProps = async context => {\n  global.kiloServerMonitoring.info('getHomeServerSideProps called');\n  ...\n};\n```\n\n#### Approach with direct instantiation\n\nIf you don’t care too much about catching native errors or native logs\nin the early stages of your server app -\nyou can avoid sharing logger via global scope and instead initialize it inside of app.\n\n```\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/server';\n\nexport const monitoring = new ServerMonitoringService({\n  authToken: MONITORING_TOOL__API_KEY,\n  serviceName: MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: MONITORING_TOOL__SERVICE_ENV,\n})\n```\n\nAs you can see we are using here all our env variables.\nIf any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n### Tracing setup:\n\nWe need to connect tracing as soon as possible during code, so it can be injected into all base modules for APM monitoring.\nTracing module is available via:\n\n```\nconst { initTracing } = require('@kilohealth/web-app-monitoring/initTracing');\ninitTracing({\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n});\n```\n\nExample(NextJS):\n\n```\nconst { PHASE_PRODUCTION_SERVER } = require('next/constants');\nconst { initTracing } = require('@kilohealth/web-app-monitoring/initTracing');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    initTracing({\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    });\n  }\n}\n```\n\n> In newer versions of NextJS there is experimental feature called\n> [instrumentationHook](https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation)\n> We can opt out from using undocumented `PHASE_PRODUCTION_SERVER` to use `instrumentationHook` for tracing init.\n\n> There is also possibility to use `NODE_OPTIONS='-r ./prestart-script.js ' next start` instead.\n> But there is an issue with `pino-datadog-transport`, which for performance reason spawns separate thread for log sending to data-dog and it this option seems to be passed to that process as well which triggers an infinite loop of require and initialization.\n\n## API\n\n### MonitoringService (both BrowserMonitoringService and ServerMonitoringService have these methods)\n\n#### debug, info, warn\n\n```\ndebug(message: string, context?: object)\ninfo(message: string, context?: object)\nwarn(message: string, context?: object)\n```\n\n- `message` - any message to be logged\n- `context` - object with all needed and related to the log entrance data\n\n#### error\n\n```\nerror(message: string, context?: object, error?: Error)\n```\n\nSame as above, but you can also optionally pass error instance as thrid parameter\n\n#### reportError\n\n```\nreportError(error: Error, context?: object)\n```\n\nShortcut for `service.error()`, which uses `error.message` field as message param for `error` method.\n\n### BrowserMonitoringService\n\n#### constructor\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```\ninterface RemoteMonitoringServiceParams {\n  serviceName?: string;\n  serviceVersion?: string;\n  serviceEnv?: string;\n  authToken?: string;\n}\n```\n\n- `RemoteMonitoringServiceConfig` - datadog params passed to init function. More info in [docs](https://docs.datadoghq.com/logs/log_collection/javascript/#initialization-parameters)\n- `serviceName` - name of the service\n- `serviceVersion` - version of the service\n- `serviceEnv` - environment where service is deployed\n- `authToken` - client token\n\n### ServerMonitoringService\n\n#### constructor\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```\ninterface RemoteMonitoringServiceConfig {\n  transportOptions?: Partial<TransportBaseOptions>;\n  loggerOptions?: Partial<LoggerOptions>;\n}\n```\n\n- `transportOptions` - [pino-datadog-transport options](https://github.com/theogravity/pino-datadog-transport#configuration-options)\n- `loggerOptions` - [pino logger options](https://getpino.io/#/docs/api?id=options-object)\n\n#### overrideLogger\n\nOverrides logger passed as argument with monitoring logger.\nAll methods of this logger will be overridden with corresponding methods of server monitoring.\n\n```\noverrideLogger(unknownLogger: UnknownLogger)\n```\n\n```\ninterface UnknownLogger {\n  log?(...parts: unknown[]): void;\n  debug?(...parts: unknown[]): void;\n  info(...parts: unknown[]): void;\n  warn(...parts: unknown[]): void;\n  error(...parts: unknown[]): void;\n}\n```\n\n#### overrideNativeConsole\n\nCalls overrideLogger for native console.\n\n```\noverrideNativeConsole()\n```\n\n#### catchProcessErrors\n\nSubscribes to `unhandledRejection` and `uncaughtException` events of the process to report `error` in such cases.\n\n```\ncatchProcessErrors()\n```\n\n### ServerMonitoringService\n\n#### initServerMonitoring\n\nInstantiate ServerMonitoringService with provided params and may also do additional work,\ndepending on provided variables.\n\n```\ninitServerMonitoring = (\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  monitoringOptions?: MonitoringOptions,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig\n): ServerMonitoringService\n```\n\n```\ninterface MonitoringOptions {\n  shouldOverrideNativeConsole?: boolean;\n  shouldCatchProcessErrors?: boolean;\n  globalMonitoringInstanceName?: string;\n}\n```\n\n- `RemoteMonitoringServiceParams` and `RemoteMonitoringServiceConfig` are same as in `constructor` api\n- `shouldOverrideNativeConsole` - if `true`, will call `serverMonitoringService.overrideNativeConsole()` under the hood\n- `shouldCatchProcessErrors` - if `true`, will call `serverMonitoringService.catchProcessErrors()` under the hood\n- `globalMonitoringInstanceName` - if provided with non-empty string will put instantiated `serverMonitoringService` into global scope under provided name.\n","readmeFilename":"README.md","gitHead":"adb19a7b84dd41f94998aeeb6109fb1f493c1d1d","description":"[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)","_id":"@kilohealth/web-app-monitoring@1.0.0-alpha.16","_nodeVersion":"18.16.0","_npmVersion":"9.6.7","dist":{"integrity":"sha512-flnX0duUYbeCh9GWprG6we8+fZNM6HVu0OPAZxLTAZME5I+MwCJMDf/yNmCVsUykHcolQV2FLGBPS2Sea3rqig==","shasum":"0242194a2fab301b84ee3650949eab197df77585","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.0.0-alpha.16.tgz","fileCount":40,"unpackedSize":53952,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCIcVQSW1Vay/fWidCapBNJT9PVCCbyoXzzudKg8j6RXwIhAL7ggzqyEpOQLItlhiwuaFOBHSkg36ptotN2b7F98Fny"}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.0.0-alpha.16_1687321695903_0.0862683502484689"},"_hasShrinkwrap":false},"1.0.0-beta.1":{"name":"@kilohealth/web-app-monitoring","version":"1.0.0-beta.1","license":"MIT","author":{"name":"Kilo Health"},"exports":{"./dist/server/initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./dist/server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./dist/browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","githooks:commit-msg":"commitlint --edit","githooks:pre-commit":"lint-staged","prepare":"husky install","semantic-release":"semantic-release","test":"jest","upload:sourcemaps":"./dist/cli/sourcemap-uploader.sh"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","@datadog/datadog-ci":"^2.11.0","dd-trace":"^4.0.0","deepmerge":"^4.3.1","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","@types/lodash":"^4.14.195","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jest-environment-jsdom":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"readme":"# @kilohealth/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\n## The Idea\n\nPackage was created to abstract away underlying level of monitoring,\nto make it easy to setup monitoring for any env as well as to make it easy to migrate from DataDog to other monitoring solution.\n\nThe package consists of 3 parts:\n\n- browser monitoring\n- CLI (needed to upload sourcemaps for browser monitoring)\n- server monitoring\n\n## Browser monitoring setup:\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n> If you are migrating from direct datadog integration - don’t forget to remove @datadog/browser-logs and @datadog/datadog-ci. Those are now deps of @kilohealth/web-app-monitoring.\n\n```\nnpm uninstall @datadog/browser-logs @datadog/datadog-ci\n```\n\n### Add build phase env variables\n\n- `MONITORING_TOOL__API_KEY` - this key is needed in order to uploaded sourcemaps for browser monitoring. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048).\n- `MONITORING_TOOL__SERVICE_NAME`, `MONITORING_TOOL__SERVICE_VERSION` and `MONITORING_TOOL__SERVICE_ENV`- the service name, version and env. These variables will be used by client code as well as by cli. Most client frameworks will not expose all node build phase env vars, so you probably need to reexpose them with prefix to switch on automatic replacement for client code during client build. In particular\n  - For NextJS - you have to add prefix `NEXT_PUBLIC_` to each of them. For example you have to add not only `MONITORING_TOOL__SERVICE_NAME=timely-hand-web-funnel-app` but also `NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME`. See more in [docs](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#exposing-environment-variables-to-the-browser).\n  - For GatsbyJS - you have to add prefix `GATSBY_`. See more in [docs](https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/#accessing-environment-variables-in-the-browser)\n  - For ViteJS - you have to add prefix `VITE_`. See more in [docs](https://vitejs.dev/guide/env-and-mode.html#env-files).\n- `MONITORING_TOOL__CLIENT_TOKEN` - this is client side token, which need to be built into client code in order to send logs into DD server.\n  Because it is needed on client you will have to re-expose it using same approach as variables above (probably prefixing env var).\n  Token you can create or find [here](https://app.datadoghq.com/organization-settings/client-tokens).\n  > PS: theoretically you can avoid creating `MONITORING_TOOL__CLIENT_TOKEN` env variable and create only `NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN` instead, because this variable is not needed for CLI to work. But for the sake of SRP we advocate for sticking with same reexposing approach here.\n\n#### Example of full env variables setup for client monitoring:\n\n##### Expose client token to be able to reexpose it for client-side code:\n\n```\nMONITORING_TOOL__CLIENT_TOKEN=pub2_your_client_token\n```\n\n##### Set variables for source map upload CLI to work during the build phase:\n\n```\nMONITORING_TOOL__SERVICE_ENV=$CI_ENVIRONMENT_NAME\n```\n\n```\nMONITORING_TOOL__SERVICE_NAME=greantess-funnel\n```\n\n```\nMONITORING_TOOL__SERVICE_VERSION=$CI_COMMIT_SHA\n```\n\n```\nMONITORING_TOOL__API_KEY=4be_your_api_key\n```\n\n##### Reexpose for your framework to client-side code(NextJS example):\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV=$MONITORING_TOOL__SERVICE_ENV\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION=$MONITORING_TOOL__SERVICE_VERSION\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN=$MONITORING_TOOL__CLIENT_TOKEN\n```\n\n### Modify your build code to generate sourcemaps, depending on env variable\n\nIdeally we don't want to generate and upload sourcemaps during each build.\nIn order to opt-in for this behavior sometimes we need to make additional configuration changes in our build process.\nWe need to build sourcemaps only in case specific env variable `IS_SOURCEMAP_UPLOAD_BUILD` is provided.\nWe don't provide it for dev or debug builds, only for production.\nThese are articles on how to do this for different frameworks and examples.\n\n- [Vite](https://vitejs.dev/config/build-options.html#build-sourcemap)\n\n```\nexport default defineConfig({\n  ...\n  build: {\n    sourcemap: Boolean(process.env.IS_SOURCEMAP_UPLOAD_BUILD),\n    ...\n  }\n  ...\n})\n```\n\n- [NextJS](https://nextjs.org/docs/pages/api-reference/next-config-js/productionBrowserSourceMaps)\n\n```\nmodule.exports = {\n  ...\n  productionBrowserSourceMaps: Boolean(process.env.IS_SOURCEMAP_UPLOAD_BUILD),\n  ...\n}\n```\n\n- Gatsby is a little bit more tricky. It generates sourcemaps by default. In order to prevent this you can add code to\n\n```\nmodule.exports.onCreateWebpackConfig = ({ stage, actions }) => {\n  // build-javascript is prod build phase\n  if (stage === 'build-javascript') {\n    actions.setWebpackConfig({\n      // hidden-source-map removes last line from final files,\n      // to avoid contenthash mismatch between builds\n      // we don't want sourcemaps in prod by default\n      devtool: process.env.IS_SOURCEMAP_UPLOAD_BUILD\n        ? 'hidden-source-map'\n        : false,\n    });\n  }\n};\n```\n\nThere is also an [article](https://akashrajpurohit.com/blog/disable-source-maps-in-gatsbyjs-v2/) with more details.\n\n### Add build and upload sourcemaps script to scripts section\n\n#### Prepare sourcemap upload build\n\nIt should run bin from our lib called `web-app-monitoring__upload-sourcemaps`.\nFor this script to work you would need to provide it with two variables\n\n- `MONITORING_TOOL__PUBLIC_PATH` - this is RELATIVE path, part of URL between domain (which can be different for different environments) and path to file itself.\n  In other words - base path for all the assets within your application.\n  You can think of this as kind of relative [Public Path | webpack](https://webpack.js.org/guides/public-path/).\n  For example it can be `/` or `/static`.\n  In other words this is common relative prefix for all your static files or / if there is none.\n  - for Vite default is `/`\n  - for NextJS default is `/_next/static/chunks` (!!! `_` instead of `.` in file system)\n  - for GatsbyJS default is `/`\n- `MONITORING_TOOL__BUILD_DIR` - this should be RELATIVE path to your build directory. For example `./dist` or `./build`.\n  - for Vite default is `./dist`\n  - for NextJS default is `./.next/static/chunks`\n  - for GatsbyJS default is `./public`\n\nExample for NextJS\n\n```\nMONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\n```\n\n#### 1. Approach with parallel build\n\nWe advocate for this approach.\nWith it you have separate script to build code for deployment and another one to make a build with sourcemaps to upload those to monitoring tool.\nWe decided to extract source map building and uploading into separate step because:\n\n- not each build may need these, and it will increase build time. For example, you may want to avoid this for dev builds.\n- we don’t want to manually alter the build (aka removing sourcemaps from it) because it is fragile and hard to maintain\n- we don’t want sourcemaps to leak into production, so we wanna separate generating them and uploading files into prod into different processes\n\nExample for NextJS\n\n```\n\"upload:sourcemaps\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 npm run build && MONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\"\n```\n\nAnd then your CI should run `upload:sourcemaps` script in parallel with main build,\nto avoid blocking and increasing main build time.\n\n#### 2. Approach with same build\n\nThere is alternative approach to tweak main build process with sourcemaps.\nWe need to do next things:\n\n- switch sourcemaps to be hidden.\n  For example instead of using option sourcemaps use hidden-sourcemaps.\n  With this we will avoid warning in console in production regarding the fact that files have a reference to sourcemaps but sourcemaps are not found.\n  We are just removing this reference during build phase.\n\n- extend usual build script with flag to include sourcemaps `IS_SOURCEMAP_UPLOAD_BUILD`,\n  script to upload them and script to remove them. For example\n\n```\n\"only-upload:sourcemaps\": \"MONITORING_TOOL__BUILD_DIR=./public MONITORING_TOOL__PUBLIC_PATH=/ web-app-monitoring__upload-sourcemaps\",\n\"remove:sourcemaps\": \"find ./public -name \\\"*.map\\\" -type f -delete\",\n\"build\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 gatsby build --prefix-paths && npm run only-upload:sourcemaps && npm run remove:sourcemaps\",\n```\n\n### Usage: Import and instantiate BrowserMonitoringService\n\n> Important note: there is no single entry point for package. You can't do smth like `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring';`\n> Reason for that is to avoid bundling server-code into client bundle and vice versa. This structure will ensure effective tree shaking during build time.\n\n> In case your bundler supports package.json `exports` field - you can also omit `dist` in path folder `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/browser';`\n\n```\nimport { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/dist/browser';\n\nexport const monitoring = new BrowserMonitoringService({\n  authToken: NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN,\n  serviceName: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV,\n})\n```\n\nAs you can see we are using here all our exposed variables. If any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n#### OPTIONAL: If you are using React you may benefit from utilizing [Error Boundaries](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary):\n\n```\nimport React, { Component, PropsWithChildren } from 'react';\nimport { monitoring } from '../services/monitoring';\n\ninterface ErrorBoundaryProps {}\ninterface ErrorBoundaryState {\n  hasError: boolean;\n}\n\nexport class ErrorBoundary extends Component<\n  PropsWithChildren<ErrorBoundaryProps>,\n  ErrorBoundaryState\n> {\n  static getDerivedStateFromError(_: Error): ErrorBoundaryState {\n    return { hasError: true };\n  }\n\n  state: ErrorBoundaryState = {\n    hasError: false,\n  };\n\n  componentDidCatch(error: Error) {\n    monitoring.reportError(error);\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return <h1>Sorry... There was an error</h1>;\n    }\n\n    return this.props.children;\n  }\n}\n```\n\n## Server monitoring setup (NextJS):\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n### Add build phase env variables\n\n- `MONITORING_TOOL__API_KEY` - this key is needed in order to send logs. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048).\n- `MONITORING_TOOL__SERVICE_NAME`, `MONITORING_TOOL__SERVICE_VERSION` and `MONITORING_TOOL__SERVICE_ENV` - the service name, version and env. These variables will be used to send logs.\n\n#### Example of full env variables setup for server monitoring:\n\n##### Set variables for sending logs:\n\n```\nMONITORING_TOOL__SERVICE_ENV=$CI_ENVIRONMENT_NAME\n```\n\n```\nMONITORING_TOOL__SERVICE_NAME=greantess-funnel\n```\n\n```\nMONITORING_TOOL__SERVICE_VERSION=$CI_COMMIT_SHA\n```\n\n```\nMONITORING_TOOL__API_KEY=4be_your_api_key\n```\n\n### Usage\n\n#### Approach with facade\n\n`initServerMonitoring` is a facade over `ServerMonitoringService`.\nYou can instantiate and use that service directly.\nThis function basically does one thing - instantiate it and can do 3 more additional things:\n\n- call `overrideNativeConsole` - method of the service to override native console, to log to datadog instead.\n- cal `catchProcessErrors` - method of the service to subscribe to native errors, to log them to datadog.\n- put service itself into global scope under defined name, so serverside code can use it.\n\nYou may wonder why we instantiate service here and not in server-side code.\nThe reason for that is if we override the native console and catch native errors - we would like to set up this as soon as possible.\nIf you don’t care too much about the very first seconds of next server - you can use alternative simpler server side logging solution.\n\nExample(for NextJS):\n\n- update next.config.ts to include into start script of production server code\n  `next.config.ts:`\n\n```\nconst { initServerMonitoring } = require('@kilohealth/web-app-monitoring/dist/server');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    const remoteMonitoringServiceParams = {\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    };\n    const config = {\n      shouldOverrideNativeConsole: true,\n      shouldCatchProcessErrors: true,\n      globalMonitoringInstanceName: 'kiloServerMonitoring',\n    }\n    initServerMonitoring(remoteMonitoringServiceParams, config);\n  }\n}\n```\n\n- update `custom.d.ts` file to declare that global scope now have monitoring service as a prop\n  In order to use ServerMonitoringService instance in other parts of code via global we need to let TS know\n  that we added new property to global object.\n  In NextJS you can just create or add next code into `custom.d.ts` file in root of the project.\n  Be aware that var name matches string that you provided in code above (kiloServerMonitoring in this case).\n  `custom.d.ts:`\n\n```\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\ndeclare global {\n  // eslint-disable-next-line no-var\n  var kiloServerMonitoring: ServerMonitoringService;\n}\n```\n\n- use it in code\n\n```\nexport const getHomeServerSideProps = async context => {\n  global.kiloServerMonitoring.info('getHomeServerSideProps called');\n  ...\n};\n```\n\n#### Approach with direct instantiation\n\nIf you don’t care too much about catching native errors or native logs\nin the early stages of your server app -\nyou can avoid sharing logger via global scope and instead initialize it inside of app.\n\n```\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\nexport const monitoring = new ServerMonitoringService({\n  authToken: MONITORING_TOOL__API_KEY,\n  serviceName: MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: MONITORING_TOOL__SERVICE_ENV,\n})\n```\n\nAs you can see we are using here all our env variables.\nIf any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n### Tracing setup:\n\nWe need to connect tracing as soon as possible during code, so it can be injected into all base modules for APM monitoring.\nTracing module is available via:\n\n```\nconst { initTracing } = require('@kilohealth/web-app-monitoring/dist/initTracing');\ninitTracing({\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n});\n```\n\nExample(NextJS):\n\n```\nconst { PHASE_PRODUCTION_SERVER } = require('next/constants');\nconst { initTracing } = require('@kilohealth/web-app-monitoring/dist/initTracing');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    initTracing({\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    });\n  }\n}\n```\n\n> In newer versions of NextJS there is experimental feature called\n> [instrumentationHook](https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation)\n> We can opt out from using undocumented `PHASE_PRODUCTION_SERVER` to use `instrumentationHook` for tracing init.\n\n> There is also possibility to use `NODE_OPTIONS='-r ./prestart-script.js ' next start` instead.\n> But there is an issue with `pino-datadog-transport`, which for performance reason spawns separate thread for log sending to data-dog and it this option seems to be passed to that process as well which triggers an infinite loop of require and initialization.\n\n## API\n\n### MonitoringService (both BrowserMonitoringService and ServerMonitoringService have these methods)\n\n#### debug, info, warn\n\n```\ndebug(message: string, context?: object)\ninfo(message: string, context?: object)\nwarn(message: string, context?: object)\n```\n\n- `message` - any message to be logged\n- `context` - object with all needed and related to the log entrance data\n\n#### error\n\n```\nerror(message: string, context?: object, error?: Error)\n```\n\nSame as above, but you can also optionally pass error instance as thrid parameter\n\n#### reportError\n\n```\nreportError(error: Error, context?: object)\n```\n\nShortcut for `service.error()`, which uses `error.message` field as message param for `error` method.\n\n### BrowserMonitoringService\n\n#### constructor\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```\ninterface RemoteMonitoringServiceParams {\n  serviceName?: string;\n  serviceVersion?: string;\n  serviceEnv?: string;\n  authToken?: string;\n}\n```\n\n- `RemoteMonitoringServiceConfig` - datadog params passed to init function. More info in [docs](https://docs.datadoghq.com/logs/log_collection/javascript/#initialization-parameters)\n- `serviceName` - name of the service\n- `serviceVersion` - version of the service\n- `serviceEnv` - environment where service is deployed\n- `authToken` - client token\n\n### ServerMonitoringService\n\n#### constructor\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```\ninterface RemoteMonitoringServiceConfig {\n  transportOptions?: Partial<TransportBaseOptions>;\n  loggerOptions?: Partial<LoggerOptions>;\n}\n```\n\n- `transportOptions` - [pino-datadog-transport options](https://github.com/theogravity/pino-datadog-transport#configuration-options)\n- `loggerOptions` - [pino logger options](https://getpino.io/#/docs/api?id=options-object)\n\n#### overrideLogger\n\nOverrides logger passed as argument with monitoring logger.\nAll methods of this logger will be overridden with corresponding methods of server monitoring.\n\n```\noverrideLogger(unknownLogger: UnknownLogger)\n```\n\n```\ninterface UnknownLogger {\n  log?(...parts: unknown[]): void;\n  debug?(...parts: unknown[]): void;\n  info(...parts: unknown[]): void;\n  warn(...parts: unknown[]): void;\n  error(...parts: unknown[]): void;\n}\n```\n\n#### overrideNativeConsole\n\nCalls overrideLogger for native console.\n\n```\noverrideNativeConsole()\n```\n\n#### catchProcessErrors\n\nSubscribes to `unhandledRejection` and `uncaughtException` events of the process to report `error` in such cases.\n\n```\ncatchProcessErrors()\n```\n\n### ServerMonitoringService\n\n#### initServerMonitoring\n\nInstantiate ServerMonitoringService with provided params and may also do additional work,\ndepending on provided variables.\n\n```\ninitServerMonitoring = (\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  monitoringOptions?: MonitoringOptions,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig\n): ServerMonitoringService\n```\n\n```\ninterface MonitoringOptions {\n  shouldOverrideNativeConsole?: boolean;\n  shouldCatchProcessErrors?: boolean;\n  globalMonitoringInstanceName?: string;\n}\n```\n\n- `RemoteMonitoringServiceParams` and `RemoteMonitoringServiceConfig` are same as in `constructor` api\n- `shouldOverrideNativeConsole` - if `true`, will call `serverMonitoringService.overrideNativeConsole()` under the hood\n- `shouldCatchProcessErrors` - if `true`, will call `serverMonitoringService.catchProcessErrors()` under the hood\n- `globalMonitoringInstanceName` - if provided with non-empty string will put instantiated `serverMonitoringService` into global scope under provided name.\n","readmeFilename":"README.md","gitHead":"ac797f9381b4447e4715936ee019bb96706640c6","description":"[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)","_id":"@kilohealth/web-app-monitoring@1.0.0-beta.1","_nodeVersion":"18.16.0","_npmVersion":"9.6.7","dist":{"integrity":"sha512-V0QUhJ05gvYMZzIlYCN7lY3Lmx1QmQb3IZIszVMYHS4nW3a0B2UdImUHEP7sg2cVvayiOF9fvs0/TqsyRQvu1g==","shasum":"853f955ec8761e1cb010e5eeeb537e55111b04d0","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.0.0-beta.1.tgz","fileCount":40,"unpackedSize":54058,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGvPExGVNJr50Mzqx7OPX856exSrtGh/V/sYhHfi/6KGAiB/bTD/k3+xneOm00f33sjvxdY1j2ToDj3M7sgZIcT7Jg=="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.0.0-beta.1_1687336131487_0.5083952868073265"},"_hasShrinkwrap":false},"1.1.0":{"name":"@kilohealth/web-app-monitoring","version":"1.1.0","license":"MIT","author":{"name":"Kilo Health"},"exports":{"./dist/server/initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./dist/server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./dist/browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","githooks:commit-msg":"commitlint --edit","githooks:pre-commit":"lint-staged","prepare":"husky install","semantic-release":"semantic-release","test":"jest","upload:sourcemaps":"./dist/cli/sourcemap-uploader.sh"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","@datadog/datadog-ci":"^2.11.0","dd-trace":"^4.0.0","deepmerge":"^4.3.1","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","@types/lodash":"^4.14.195","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jest-environment-jsdom":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"gitHead":"0512673e07d29b7d3e49a67af5a85f6a0c3bf58c","description":"[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)","_id":"@kilohealth/web-app-monitoring@1.1.0","_nodeVersion":"18.16.0","_npmVersion":"9.6.7","dist":{"integrity":"sha512-jbYGL52IBJVEWIcMzJD6SkjhvfVYnHU7S+RiYy3fU9TPiBs/TlnznV7ce+8zQLZg36r2T5CqO7YWB7pEIgHwaQ==","shasum":"70779e7bb837e57fe7584d181fad035216e9e6f3","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.1.0.tgz","fileCount":40,"unpackedSize":54051,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFxa1fS4Beu6b9Sp2nKnxC2bUKE2RjDmVhxUEV3QBhplAiEAsFYbQeeyBSzJu8PgsAG598oll+7qv3AmDbEPRE9rbF4="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.1.0_1687340037990_0.5424303773929515"},"_hasShrinkwrap":false},"1.2.0-alpha.1":{"name":"@kilohealth/web-app-monitoring","version":"1.2.0-alpha.1","license":"MIT","author":{"name":"Kilo Health"},"exports":{"./dist/server/initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./dist/server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./dist/browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","githooks:commit-msg":"commitlint --edit","githooks:pre-commit":"lint-staged","prepare":"husky install","semantic-release":"semantic-release","test":"jest","upload:sourcemaps":"./dist/cli/sourcemap-uploader.sh"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","@datadog/datadog-ci":"^2.11.0","dd-trace":"^4.0.0","deepmerge":"^4.3.1","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","@types/lodash":"^4.14.195","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jest-environment-jsdom":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"readme":"# @kilohealth/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\n## The Idea\n\nPackage was created to abstract away underlying level of monitoring,\nto make it easy to setup monitoring for any env as well as to make it easy to migrate from DataDog to other monitoring solution.\n\nThe package consists of 3 parts:\n\n- browser monitoring\n- CLI (needed to upload sourcemaps for browser monitoring)\n- server monitoring\n\n## Browser monitoring setup:\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n> If you are migrating from direct datadog integration - don’t forget to remove @datadog/browser-logs and @datadog/datadog-ci. Those are now deps of @kilohealth/web-app-monitoring.\n\n```\nnpm uninstall @datadog/browser-logs @datadog/datadog-ci\n```\n\n### Add build phase env variables\n\n- `MONITORING_TOOL__API_KEY` - this key is needed in order to uploaded sourcemaps for browser monitoring. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048).\n- `MONITORING_TOOL__SERVICE_NAME`, `MONITORING_TOOL__SERVICE_VERSION` and `MONITORING_TOOL__SERVICE_ENV`- the service name, version and env. These variables will be used by client code as well as by cli. Most client frameworks will not expose all node build phase env vars, so you probably need to reexpose them with prefix to switch on automatic replacement for client code during client build. In particular\n  - For NextJS - you have to add prefix `NEXT_PUBLIC_` to each of them. For example you have to add not only `MONITORING_TOOL__SERVICE_NAME=timely-hand-web-funnel-app` but also `NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME`. See more in [docs](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#exposing-environment-variables-to-the-browser).\n  - For GatsbyJS - you have to add prefix `GATSBY_`. See more in [docs](https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/#accessing-environment-variables-in-the-browser)\n  - For ViteJS - you have to add prefix `VITE_`. See more in [docs](https://vitejs.dev/guide/env-and-mode.html#env-files).\n- `MONITORING_TOOL__CLIENT_TOKEN` - this is client side token, which need to be built into client code in order to send logs into DD server.\n  Because it is needed on client you will have to re-expose it using same approach as variables above (probably prefixing env var).\n  Token you can create or find [here](https://app.datadoghq.com/organization-settings/client-tokens).\n  > PS: theoretically you can avoid creating `MONITORING_TOOL__CLIENT_TOKEN` env variable and create only `NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN` instead, because this variable is not needed for CLI to work. But for the sake of SRP we advocate for sticking with same reexposing approach here.\n\n#### Example of full env variables setup for client monitoring:\n\n##### Expose client token to be able to reexpose it for client-side code:\n\n```\nMONITORING_TOOL__CLIENT_TOKEN=pub2_your_client_token\n```\n\n##### Set variables for source map upload CLI to work during the build phase:\n\n```\nMONITORING_TOOL__SERVICE_ENV=$CI_ENVIRONMENT_NAME\n```\n\n```\nMONITORING_TOOL__SERVICE_NAME=greantess-funnel\n```\n\n```\nMONITORING_TOOL__SERVICE_VERSION=$CI_COMMIT_SHA\n```\n\n```\nMONITORING_TOOL__API_KEY=4be_your_api_key\n```\n\n##### Reexpose for your framework to client-side code(NextJS example):\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV=$MONITORING_TOOL__SERVICE_ENV\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION=$MONITORING_TOOL__SERVICE_VERSION\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN=$MONITORING_TOOL__CLIENT_TOKEN\n```\n\n### Modify your build code to generate sourcemaps, depending on env variable\n\nIdeally we don't want to generate and upload sourcemaps during each build.\nIn order to opt-in for this behavior sometimes we need to make additional configuration changes in our build process.\nWe need to build sourcemaps only in case specific env variable `IS_SOURCEMAP_UPLOAD_BUILD` is provided.\nWe don't provide it for dev or debug builds, only for production.\nThese are articles on how to do this for different frameworks and examples.\n\n- [Vite](https://vitejs.dev/config/build-options.html#build-sourcemap)\n\n```\nexport default defineConfig({\n  ...\n  build: {\n    sourcemap: Boolean(process.env.IS_SOURCEMAP_UPLOAD_BUILD),\n    ...\n  }\n  ...\n})\n```\n\n- [NextJS](https://nextjs.org/docs/pages/api-reference/next-config-js/productionBrowserSourceMaps)\n\n```\nmodule.exports = {\n  ...\n  productionBrowserSourceMaps: Boolean(process.env.IS_SOURCEMAP_UPLOAD_BUILD),\n  ...\n}\n```\n\n- Gatsby is a little bit more tricky. It generates sourcemaps by default. In order to prevent this you can add code to\n\n```\nmodule.exports.onCreateWebpackConfig = ({ stage, actions }) => {\n  // build-javascript is prod build phase\n  if (stage === 'build-javascript') {\n    actions.setWebpackConfig({\n      // hidden-source-map removes last line from final files,\n      // to avoid contenthash mismatch between builds\n      // we don't want sourcemaps in prod by default\n      devtool: process.env.IS_SOURCEMAP_UPLOAD_BUILD\n        ? 'hidden-source-map'\n        : false,\n    });\n  }\n};\n```\n\nThere is also an [article](https://akashrajpurohit.com/blog/disable-source-maps-in-gatsbyjs-v2/) with more details.\n\n### Add build and upload sourcemaps script to scripts section\n\n#### Prepare sourcemap upload build\n\nIt should run bin from our lib called `web-app-monitoring__upload-sourcemaps`.\nFor this script to work you would need to provide it with two variables\n\n- `MONITORING_TOOL__PUBLIC_PATH` - this is RELATIVE path, part of URL between domain (which can be different for different environments) and path to file itself.\n  In other words - base path for all the assets within your application.\n  You can think of this as kind of relative [Public Path | webpack](https://webpack.js.org/guides/public-path/).\n  For example it can be `/` or `/static`.\n  In other words this is common relative prefix for all your static files or / if there is none.\n  - for Vite default is `/`\n  - for NextJS default is `/_next/static/chunks` (!!! `_` instead of `.` in file system)\n  - for GatsbyJS default is `/`\n- `MONITORING_TOOL__BUILD_DIR` - this should be RELATIVE path to your build directory. For example `./dist` or `./build`.\n  - for Vite default is `./dist`\n  - for NextJS default is `./.next/static/chunks`\n  - for GatsbyJS default is `./public`\n\nExample for NextJS\n\n```\nMONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\n```\n\n#### 1. Approach with parallel build\n\nWe advocate for this approach.\nWith it you have separate script to build code for deployment and another one to make a build with sourcemaps to upload those to monitoring tool.\nWe decided to extract source map building and uploading into separate step because:\n\n- not each build may need these, and it will increase build time. For example, you may want to avoid this for dev builds.\n- we don’t want to manually alter the build (aka removing sourcemaps from it) because it is fragile and hard to maintain\n- we don’t want sourcemaps to leak into production, so we wanna separate generating them and uploading files into prod into different processes\n\nExample for NextJS\n\n```\n\"upload:sourcemaps\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 npm run build && MONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\"\n```\n\nAnd then your CI should run `upload:sourcemaps` script in parallel with main build,\nto avoid blocking and increasing main build time.\n\n#### 2. Approach with same build\n\nThere is alternative approach to tweak main build process with sourcemaps.\nWe need to do next things:\n\n- switch sourcemaps to be hidden.\n  For example instead of using option sourcemaps use hidden-sourcemaps.\n  With this we will avoid warning in console in production regarding the fact that files have a reference to sourcemaps but sourcemaps are not found.\n  We are just removing this reference during build phase.\n\n- extend usual build script with flag to include sourcemaps `IS_SOURCEMAP_UPLOAD_BUILD`,\n  script to upload them and script to remove them. For example\n\n```\n\"only-upload:sourcemaps\": \"MONITORING_TOOL__BUILD_DIR=./public MONITORING_TOOL__PUBLIC_PATH=/ web-app-monitoring__upload-sourcemaps\",\n\"remove:sourcemaps\": \"find ./public -name \\\"*.map\\\" -type f -delete\",\n\"build\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 gatsby build --prefix-paths && npm run only-upload:sourcemaps && npm run remove:sourcemaps\",\n```\n\n### Usage: Import and instantiate BrowserMonitoringService\n\n> Important note: there is no single entry point for package. You can't do smth like `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring';`\n> Reason for that is to avoid bundling server-code into client bundle and vice versa. This structure will ensure effective tree shaking during build time.\n\n> In case your bundler supports package.json `exports` field - you can also omit `dist` in path folder `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/browser';`\n\n```\nimport { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/dist/browser';\n\nexport const monitoring = new BrowserMonitoringService({\n  authToken: NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN,\n  serviceName: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV,\n})\n```\n\nAs you can see we are using here all our exposed variables. If any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n#### OPTIONAL: If you are using React you may benefit from utilizing [Error Boundaries](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary):\n\n```\nimport React, { Component, PropsWithChildren } from 'react';\nimport { monitoring } from '../services/monitoring';\n\ninterface ErrorBoundaryProps {}\ninterface ErrorBoundaryState {\n  hasError: boolean;\n}\n\nexport class ErrorBoundary extends Component<\n  PropsWithChildren<ErrorBoundaryProps>,\n  ErrorBoundaryState\n> {\n  static getDerivedStateFromError(_: Error): ErrorBoundaryState {\n    return { hasError: true };\n  }\n\n  state: ErrorBoundaryState = {\n    hasError: false,\n  };\n\n  componentDidCatch(error: Error) {\n    monitoring.reportError(error);\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return <h1>Sorry... There was an error</h1>;\n    }\n\n    return this.props.children;\n  }\n}\n```\n\n## Server monitoring setup (NextJS):\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n### Add build phase env variables\n\n- `MONITORING_TOOL__API_KEY` - this key is needed in order to send logs. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048).\n- `MONITORING_TOOL__SERVICE_NAME`, `MONITORING_TOOL__SERVICE_VERSION` and `MONITORING_TOOL__SERVICE_ENV` - the service name, version and env. These variables will be used to send logs.\n\n#### Example of full env variables setup for server monitoring:\n\n##### Set variables for sending logs:\n\n```\nMONITORING_TOOL__SERVICE_ENV=$CI_ENVIRONMENT_NAME\n```\n\n```\nMONITORING_TOOL__SERVICE_NAME=greantess-funnel\n```\n\n```\nMONITORING_TOOL__SERVICE_VERSION=$CI_COMMIT_SHA\n```\n\n```\nMONITORING_TOOL__API_KEY=4be_your_api_key\n```\n\n### Usage\n\n#### Approach with facade\n\n`initServerMonitoring` is a facade over `ServerMonitoringService`.\nYou can instantiate and use that service directly.\nThis function basically does one thing - instantiate it and can do 3 more additional things:\n\n- call `overrideNativeConsole` - method of the service to override native console, to log to datadog instead.\n- cal `catchProcessErrors` - method of the service to subscribe to native errors, to log them to datadog.\n- put service itself into global scope under defined name, so serverside code can use it.\n\nYou may wonder why we instantiate service here and not in server-side code.\nThe reason for that is if we override the native console and catch native errors - we would like to set up this as soon as possible.\nIf you don’t care too much about the very first seconds of next server - you can use alternative simpler server side logging solution.\n\nExample(for NextJS):\n\n- update next.config.ts to include into start script of production server code\n  `next.config.ts:`\n\n```\nconst { initServerMonitoring } = require('@kilohealth/web-app-monitoring/dist/server');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    const remoteMonitoringServiceParams = {\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    };\n    const config = {\n      shouldOverrideNativeConsole: true,\n      shouldCatchProcessErrors: true,\n      globalMonitoringInstanceName: 'kiloServerMonitoring',\n    }\n    initServerMonitoring(remoteMonitoringServiceParams, config);\n  }\n}\n```\n\n- update `custom.d.ts` file to declare that global scope now have monitoring service as a prop\n  In order to use ServerMonitoringService instance in other parts of code via global we need to let TS know\n  that we added new property to global object.\n  In NextJS you can just create or add next code into `custom.d.ts` file in root of the project.\n  Be aware that var name matches string that you provided in code above (kiloServerMonitoring in this case).\n  `custom.d.ts:`\n\n```\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\ndeclare global {\n  // eslint-disable-next-line no-var\n  var kiloServerMonitoring: ServerMonitoringService;\n}\n```\n\n- use it in code\n\n```\nexport const getHomeServerSideProps = async context => {\n  global.kiloServerMonitoring.info('getHomeServerSideProps called');\n  ...\n};\n```\n\n#### Approach with direct instantiation\n\nIf you don’t care too much about catching native errors or native logs\nin the early stages of your server app -\nyou can avoid sharing logger via global scope and instead initialize it inside of app.\n\n```\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\nexport const monitoring = new ServerMonitoringService({\n  authToken: MONITORING_TOOL__API_KEY,\n  serviceName: MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: MONITORING_TOOL__SERVICE_ENV,\n})\n```\n\nAs you can see we are using here all our env variables.\nIf any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n### Tracing setup:\n\nWe need to connect tracing as soon as possible during code, so it can be injected into all base modules for APM monitoring.\nTracing module is available via:\n\n```\nconst { initTracing } = require('@kilohealth/web-app-monitoring/dist/initTracing');\ninitTracing({\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n});\n```\n\nExample(NextJS):\n\n```\nconst { PHASE_PRODUCTION_SERVER } = require('next/constants');\nconst { initTracing } = require('@kilohealth/web-app-monitoring/dist/initTracing');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    initTracing({\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    });\n  }\n}\n```\n\n> In newer versions of NextJS there is experimental feature called\n> [instrumentationHook](https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation)\n> We can opt out from using undocumented `PHASE_PRODUCTION_SERVER` to use `instrumentationHook` for tracing init.\n\n> There is also possibility to use `NODE_OPTIONS='-r ./prestart-script.js ' next start` instead.\n> But there is an issue with `pino-datadog-transport`, which for performance reason spawns separate thread for log sending to data-dog and it this option seems to be passed to that process as well which triggers an infinite loop of require and initialization.\n\n## API\n\n### MonitoringService (both BrowserMonitoringService and ServerMonitoringService have these methods)\n\n#### debug, info, warn\n\n```\ndebug(message: string, context?: object)\ninfo(message: string, context?: object)\nwarn(message: string, context?: object)\n```\n\n- `message` - any message to be logged\n- `context` - object with all needed and related to the log entrance data\n\n#### error\n\n```\nerror(message: string, context?: object, error?: Error)\n```\n\nSame as above, but you can also optionally pass error instance as thrid parameter\n\n#### reportError\n\n```\nreportError(error: Error, context?: object)\n```\n\nShortcut for `service.error()`, which uses `error.message` field as message param for `error` method.\n\n### BrowserMonitoringService\n\n#### constructor\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```\ninterface RemoteMonitoringServiceParams {\n  serviceName?: string;\n  serviceVersion?: string;\n  serviceEnv?: string;\n  authToken?: string;\n}\n```\n\n- `RemoteMonitoringServiceConfig` - datadog params passed to init function. More info in [docs](https://docs.datadoghq.com/logs/log_collection/javascript/#initialization-parameters)\n- `serviceName` - name of the service\n- `serviceVersion` - version of the service\n- `serviceEnv` - environment where service is deployed\n- `authToken` - client token\n\n### ServerMonitoringService\n\n#### constructor\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```\ninterface RemoteMonitoringServiceConfig {\n  transportOptions?: Partial<TransportBaseOptions>;\n  loggerOptions?: Partial<LoggerOptions>;\n}\n```\n\n- `transportOptions` - [pino-datadog-transport options](https://github.com/theogravity/pino-datadog-transport#configuration-options)\n- `loggerOptions` - [pino logger options](https://getpino.io/#/docs/api?id=options-object)\n\n#### overrideLogger\n\nOverrides logger passed as argument with monitoring logger.\nAll methods of this logger will be overridden with corresponding methods of server monitoring.\n\n```\noverrideLogger(unknownLogger: UnknownLogger)\n```\n\n```\ninterface UnknownLogger {\n  log?(...parts: unknown[]): void;\n  debug?(...parts: unknown[]): void;\n  info(...parts: unknown[]): void;\n  warn(...parts: unknown[]): void;\n  error(...parts: unknown[]): void;\n}\n```\n\n#### overrideNativeConsole\n\nCalls overrideLogger for native console.\n\n```\noverrideNativeConsole()\n```\n\n#### catchProcessErrors\n\nSubscribes to `unhandledRejection` and `uncaughtException` events of the process to report `error` in such cases.\n\n```\ncatchProcessErrors()\n```\n\n### ServerMonitoringService\n\n#### initServerMonitoring\n\nInstantiate ServerMonitoringService with provided params and may also do additional work,\ndepending on provided variables.\n\n```\ninitServerMonitoring = (\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  monitoringOptions?: MonitoringOptions,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig\n): ServerMonitoringService\n```\n\n```\ninterface MonitoringOptions {\n  shouldOverrideNativeConsole?: boolean;\n  shouldCatchProcessErrors?: boolean;\n  globalMonitoringInstanceName?: string;\n}\n```\n\n- `RemoteMonitoringServiceParams` and `RemoteMonitoringServiceConfig` are same as in `constructor` api\n- `shouldOverrideNativeConsole` - if `true`, will call `serverMonitoringService.overrideNativeConsole()` under the hood\n- `shouldCatchProcessErrors` - if `true`, will call `serverMonitoringService.catchProcessErrors()` under the hood\n- `globalMonitoringInstanceName` - if provided with non-empty string will put instantiated `serverMonitoringService` into global scope under provided name.\n","readmeFilename":"README.md","gitHead":"b6670018aafd4822f7d8b230698d0a3d6dc24d9a","description":"[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)","_id":"@kilohealth/web-app-monitoring@1.2.0-alpha.1","_nodeVersion":"18.16.0","_npmVersion":"9.6.7","dist":{"integrity":"sha512-tMyO73fAyok4otiNee8Os8uENwjyLE8Tdq1azRHOTxy+Dit9BPrBaDFhyvswVzQWRlSIzN8EUNiU8/Rv7r7hcw==","shasum":"428a3837e05645decd62d9d1c619a54c414de149","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.2.0-alpha.1.tgz","fileCount":40,"unpackedSize":54068,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCqPOlwpWilso8NBcwns5rpdu/D88aa7esDhA23UMBEmAIhAPGSEzMV8Y1idt5Rf4ShEZA4tXgXOawoZmc+rEIJCfgd"}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.2.0-alpha.1_1687502679328_0.5106279071031985"},"_hasShrinkwrap":false},"1.2.0-alpha.2":{"name":"@kilohealth/web-app-monitoring","version":"1.2.0-alpha.2","license":"MIT","author":{"name":"Kilo Health"},"exports":{"./dist/server/initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./dist/server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./dist/browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","githooks:commit-msg":"commitlint --edit","githooks:pre-commit":"lint-staged","prepare":"husky install","semantic-release":"semantic-release","test":"jest","upload:sourcemaps":"./dist/cli/sourcemap-uploader.sh"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","@datadog/datadog-ci":"^2.11.0","dd-trace":"^4.0.0","deepmerge":"^4.3.1","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","@types/lodash":"^4.14.195","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jest-environment-jsdom":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"readme":"# @kilohealth/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\n## The Idea\n\nPackage was created to abstract away underlying level of monitoring,\nto make it easy to setup monitoring for any env as well as to make it easy to migrate from DataDog to other monitoring solution.\n\nThe package consists of 3 parts:\n\n- browser monitoring\n- CLI (needed to upload sourcemaps for browser monitoring)\n- server monitoring\n\n## Browser monitoring setup:\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n> If you are migrating from direct datadog integration - don’t forget to remove @datadog/browser-logs and @datadog/datadog-ci. Those are now deps of @kilohealth/web-app-monitoring.\n\n```\nnpm uninstall @datadog/browser-logs @datadog/datadog-ci\n```\n\n### Add build phase env variables\n\n- `MONITORING_TOOL__API_KEY` - this key is needed in order to uploaded sourcemaps for browser monitoring. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048).\n- `MONITORING_TOOL__SERVICE_NAME`, `MONITORING_TOOL__SERVICE_VERSION` and `MONITORING_TOOL__SERVICE_ENV`- the service name, version and env. These variables will be used by client code as well as by cli. Most client frameworks will not expose all node build phase env vars, so you probably need to reexpose them with prefix to switch on automatic replacement for client code during client build. In particular\n  - For NextJS - you have to add prefix `NEXT_PUBLIC_` to each of them. For example you have to add not only `MONITORING_TOOL__SERVICE_NAME=timely-hand-web-funnel-app` but also `NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME`. See more in [docs](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#exposing-environment-variables-to-the-browser).\n  - For GatsbyJS - you have to add prefix `GATSBY_`. See more in [docs](https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/#accessing-environment-variables-in-the-browser)\n  - For ViteJS - you have to add prefix `VITE_`. See more in [docs](https://vitejs.dev/guide/env-and-mode.html#env-files).\n- `MONITORING_TOOL__CLIENT_TOKEN` - this is client side token, which need to be built into client code in order to send logs into DD server.\n  Because it is needed on client you will have to re-expose it using same approach as variables above (probably prefixing env var).\n  Token you can create or find [here](https://app.datadoghq.com/organization-settings/client-tokens).\n  > PS: theoretically you can avoid creating `MONITORING_TOOL__CLIENT_TOKEN` env variable and create only `NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN` instead, because this variable is not needed for CLI to work. But for the sake of SRP we advocate for sticking with same reexposing approach here.\n\n#### Example of full env variables setup for client monitoring:\n\n##### Expose client token to be able to reexpose it for client-side code:\n\n```\nMONITORING_TOOL__CLIENT_TOKEN=pub2_your_client_token\n```\n\n##### Set variables for source map upload CLI to work during the build phase:\n\n```\nMONITORING_TOOL__SERVICE_ENV=$CI_ENVIRONMENT_NAME\n```\n\n```\nMONITORING_TOOL__SERVICE_NAME=greantess-funnel\n```\n\n```\nMONITORING_TOOL__SERVICE_VERSION=$CI_COMMIT_SHA\n```\n\n```\nMONITORING_TOOL__API_KEY=4be_your_api_key\n```\n\n##### Reexpose for your framework to client-side code(NextJS example):\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV=$MONITORING_TOOL__SERVICE_ENV\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION=$MONITORING_TOOL__SERVICE_VERSION\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN=$MONITORING_TOOL__CLIENT_TOKEN\n```\n\n### Modify your build code to generate sourcemaps, depending on env variable\n\nIdeally we don't want to generate and upload sourcemaps during each build.\nIn order to opt-in for this behavior sometimes we need to make additional configuration changes in our build process.\nWe need to build sourcemaps only in case specific env variable `IS_SOURCEMAP_UPLOAD_BUILD` is provided.\nWe don't provide it for dev or debug builds, only for production.\nThese are articles on how to do this for different frameworks and examples.\n\n- [Vite](https://vitejs.dev/config/build-options.html#build-sourcemap)\n\n```\nexport default defineConfig({\n  ...\n  build: {\n    sourcemap: Boolean(process.env.IS_SOURCEMAP_UPLOAD_BUILD),\n    ...\n  }\n  ...\n})\n```\n\n- [NextJS](https://nextjs.org/docs/pages/api-reference/next-config-js/productionBrowserSourceMaps)\n\n```\nmodule.exports = {\n  ...\n  productionBrowserSourceMaps: Boolean(process.env.IS_SOURCEMAP_UPLOAD_BUILD),\n  ...\n}\n```\n\n- Gatsby is a little bit more tricky. It generates sourcemaps by default. In order to prevent this you can add code to\n\n```\nmodule.exports.onCreateWebpackConfig = ({ stage, actions }) => {\n  // build-javascript is prod build phase\n  if (stage === 'build-javascript') {\n    actions.setWebpackConfig({\n      // hidden-source-map removes last line from final files,\n      // to avoid contenthash mismatch between builds\n      // we don't want sourcemaps in prod by default\n      devtool: process.env.IS_SOURCEMAP_UPLOAD_BUILD\n        ? 'hidden-source-map'\n        : false,\n    });\n  }\n};\n```\n\nThere is also an [article](https://akashrajpurohit.com/blog/disable-source-maps-in-gatsbyjs-v2/) with more details.\n\n### Add build and upload sourcemaps script to scripts section\n\n#### Prepare sourcemap upload build\n\nIt should run bin from our lib called `web-app-monitoring__upload-sourcemaps`.\nFor this script to work you would need to provide it with two variables\n\n- `MONITORING_TOOL__PUBLIC_PATH` - this is RELATIVE path, part of URL between domain (which can be different for different environments) and path to file itself.\n  In other words - base path for all the assets within your application.\n  You can think of this as kind of relative [Public Path | webpack](https://webpack.js.org/guides/public-path/).\n  For example it can be `/` or `/static`.\n  In other words this is common relative prefix for all your static files or / if there is none.\n  - for Vite default is `/`\n  - for NextJS default is `/_next/static/chunks` (!!! `_` instead of `.` in file system)\n  - for GatsbyJS default is `/`\n- `MONITORING_TOOL__BUILD_DIR` - this should be RELATIVE path to your build directory. For example `./dist` or `./build`.\n  - for Vite default is `./dist`\n  - for NextJS default is `./.next/static/chunks`\n  - for GatsbyJS default is `./public`\n\nExample for NextJS\n\n```\nMONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\n```\n\n#### 1. Approach with parallel build\n\nWe advocate for this approach.\nWith it you have separate script to build code for deployment and another one to make a build with sourcemaps to upload those to monitoring tool.\nWe decided to extract source map building and uploading into separate step because:\n\n- not each build may need these, and it will increase build time. For example, you may want to avoid this for dev builds.\n- we don’t want to manually alter the build (aka removing sourcemaps from it) because it is fragile and hard to maintain\n- we don’t want sourcemaps to leak into production, so we wanna separate generating them and uploading files into prod into different processes\n\nExample for NextJS\n\n```\n\"upload:sourcemaps\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 npm run build && MONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\"\n```\n\nAnd then your CI should run `upload:sourcemaps` script in parallel with main build,\nto avoid blocking and increasing main build time.\n\n#### 2. Approach with same build\n\nThere is alternative approach to tweak main build process with sourcemaps.\nWe need to do next things:\n\n- switch sourcemaps to be hidden.\n  For example instead of using option sourcemaps use hidden-sourcemaps.\n  With this we will avoid warning in console in production regarding the fact that files have a reference to sourcemaps but sourcemaps are not found.\n  We are just removing this reference during build phase.\n\n- extend usual build script with flag to include sourcemaps `IS_SOURCEMAP_UPLOAD_BUILD`,\n  script to upload them and script to remove them. For example\n\n```\n\"only-upload:sourcemaps\": \"MONITORING_TOOL__BUILD_DIR=./public MONITORING_TOOL__PUBLIC_PATH=/ web-app-monitoring__upload-sourcemaps\",\n\"remove:sourcemaps\": \"find ./public -name \\\"*.map\\\" -type f -delete\",\n\"build\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 gatsby build --prefix-paths && npm run only-upload:sourcemaps && npm run remove:sourcemaps\",\n```\n\n### Usage: Import and instantiate BrowserMonitoringService\n\n> Important note: there is no single entry point for package. You can't do smth like `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring';`\n> Reason for that is to avoid bundling server-code into client bundle and vice versa. This structure will ensure effective tree shaking during build time.\n\n> In case your bundler supports package.json `exports` field - you can also omit `dist` in path folder `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/browser';`\n\n```\nimport { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/dist/browser';\n\nexport const monitoring = new BrowserMonitoringService({\n  authToken: NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN,\n  serviceName: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV,\n})\n```\n\nAs you can see we are using here all our exposed variables. If any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n#### OPTIONAL: If you are using React you may benefit from utilizing [Error Boundaries](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary):\n\n```\nimport React, { Component, PropsWithChildren } from 'react';\nimport { monitoring } from '../services/monitoring';\n\ninterface ErrorBoundaryProps {}\ninterface ErrorBoundaryState {\n  hasError: boolean;\n}\n\nexport class ErrorBoundary extends Component<\n  PropsWithChildren<ErrorBoundaryProps>,\n  ErrorBoundaryState\n> {\n  static getDerivedStateFromError(_: Error): ErrorBoundaryState {\n    return { hasError: true };\n  }\n\n  state: ErrorBoundaryState = {\n    hasError: false,\n  };\n\n  componentDidCatch(error: Error) {\n    monitoring.reportError(error);\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return <h1>Sorry... There was an error</h1>;\n    }\n\n    return this.props.children;\n  }\n}\n```\n\n## Server monitoring setup (NextJS):\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n### Add build phase env variables\n\n- `MONITORING_TOOL__API_KEY` - this key is needed in order to send logs. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048).\n- `MONITORING_TOOL__SERVICE_NAME`, `MONITORING_TOOL__SERVICE_VERSION` and `MONITORING_TOOL__SERVICE_ENV` - the service name, version and env. These variables will be used to send logs.\n\n#### Example of full env variables setup for server monitoring:\n\n##### Set variables for sending logs:\n\n```\nMONITORING_TOOL__SERVICE_ENV=$CI_ENVIRONMENT_NAME\n```\n\n```\nMONITORING_TOOL__SERVICE_NAME=greantess-funnel\n```\n\n```\nMONITORING_TOOL__SERVICE_VERSION=$CI_COMMIT_SHA\n```\n\n```\nMONITORING_TOOL__API_KEY=4be_your_api_key\n```\n\n### Usage\n\n#### Approach with facade\n\n`initServerMonitoring` is a facade over `ServerMonitoringService`.\nYou can instantiate and use that service directly.\nThis function basically does one thing - instantiate it and can do 3 more additional things:\n\n- call `overrideNativeConsole` - method of the service to override native console, to log to datadog instead.\n- cal `catchProcessErrors` - method of the service to subscribe to native errors, to log them to datadog.\n- put service itself into global scope under defined name, so serverside code can use it.\n\nYou may wonder why we instantiate service here and not in server-side code.\nThe reason for that is if we override the native console and catch native errors - we would like to set up this as soon as possible.\nIf you don’t care too much about the very first seconds of next server - you can use alternative simpler server side logging solution.\n\nExample(for NextJS):\n\n- update next.config.ts to include into start script of production server code\n  `next.config.ts:`\n\n```\nconst { initServerMonitoring } = require('@kilohealth/web-app-monitoring/dist/server');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    const remoteMonitoringServiceParams = {\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    };\n    const config = {\n      shouldOverrideNativeConsole: true,\n      shouldCatchProcessErrors: true,\n      globalMonitoringInstanceName: 'kiloServerMonitoring',\n    }\n    initServerMonitoring(remoteMonitoringServiceParams, config);\n  }\n}\n```\n\n- update `custom.d.ts` file to declare that global scope now have monitoring service as a prop\n  In order to use ServerMonitoringService instance in other parts of code via global we need to let TS know\n  that we added new property to global object.\n  In NextJS you can just create or add next code into `custom.d.ts` file in root of the project.\n  Be aware that var name matches string that you provided in code above (kiloServerMonitoring in this case).\n  `custom.d.ts:`\n\n```\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\ndeclare global {\n  // eslint-disable-next-line no-var\n  var kiloServerMonitoring: ServerMonitoringService;\n}\n```\n\n- use it in code\n\n```\nexport const getHomeServerSideProps = async context => {\n  global.kiloServerMonitoring.info('getHomeServerSideProps called');\n  ...\n};\n```\n\n#### Approach with direct instantiation\n\nIf you don’t care too much about catching native errors or native logs\nin the early stages of your server app -\nyou can avoid sharing logger via global scope and instead initialize it inside of app.\n\n```\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\nexport const monitoring = new ServerMonitoringService({\n  authToken: MONITORING_TOOL__API_KEY,\n  serviceName: MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: MONITORING_TOOL__SERVICE_ENV,\n})\n```\n\nAs you can see we are using here all our env variables.\nIf any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n### Tracing setup:\n\nWe need to connect tracing as soon as possible during code, so it can be injected into all base modules for APM monitoring.\nTracing module is available via:\n\n```\nconst { initTracing } = require('@kilohealth/web-app-monitoring/dist/initTracing');\ninitTracing({\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n});\n```\n\nExample(NextJS):\n\n```\nconst { PHASE_PRODUCTION_SERVER } = require('next/constants');\nconst { initTracing } = require('@kilohealth/web-app-monitoring/dist/initTracing');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    initTracing({\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    });\n  }\n}\n```\n\n> In newer versions of NextJS there is experimental feature called\n> [instrumentationHook](https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation)\n> We can opt out from using undocumented `PHASE_PRODUCTION_SERVER` to use `instrumentationHook` for tracing init.\n\n> There is also possibility to use `NODE_OPTIONS='-r ./prestart-script.js ' next start` instead.\n> But there is an issue with `pino-datadog-transport`, which for performance reason spawns separate thread for log sending to data-dog and it this option seems to be passed to that process as well which triggers an infinite loop of require and initialization.\n\n## API\n\n### MonitoringService (both BrowserMonitoringService and ServerMonitoringService have these methods)\n\n#### debug, info, warn\n\n```\ndebug(message: string, context?: object)\ninfo(message: string, context?: object)\nwarn(message: string, context?: object)\n```\n\n- `message` - any message to be logged\n- `context` - object with all needed and related to the log entrance data\n\n#### error\n\n```\nerror(message: string, context?: object, error?: Error)\n```\n\nSame as above, but you can also optionally pass error instance as thrid parameter\n\n#### reportError\n\n```\nreportError(error: Error, context?: object)\n```\n\nShortcut for `service.error()`, which uses `error.message` field as message param for `error` method.\n\n### BrowserMonitoringService\n\n#### constructor\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```\ninterface RemoteMonitoringServiceParams {\n  serviceName?: string;\n  serviceVersion?: string;\n  serviceEnv?: string;\n  authToken?: string;\n}\n```\n\n- `RemoteMonitoringServiceConfig` - datadog params passed to init function. More info in [docs](https://docs.datadoghq.com/logs/log_collection/javascript/#initialization-parameters)\n- `serviceName` - name of the service\n- `serviceVersion` - version of the service\n- `serviceEnv` - environment where service is deployed\n- `authToken` - client token\n\n### ServerMonitoringService\n\n#### constructor\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```\ninterface RemoteMonitoringServiceConfig {\n  transportOptions?: Partial<TransportBaseOptions>;\n  loggerOptions?: Partial<LoggerOptions>;\n}\n```\n\n- `transportOptions` - [pino-datadog-transport options](https://github.com/theogravity/pino-datadog-transport#configuration-options)\n- `loggerOptions` - [pino logger options](https://getpino.io/#/docs/api?id=options-object)\n\n#### overrideLogger\n\nOverrides logger passed as argument with monitoring logger.\nAll methods of this logger will be overridden with corresponding methods of server monitoring.\n\n```\noverrideLogger(unknownLogger: UnknownLogger)\n```\n\n```\ninterface UnknownLogger {\n  log?(...parts: unknown[]): void;\n  debug?(...parts: unknown[]): void;\n  info(...parts: unknown[]): void;\n  warn(...parts: unknown[]): void;\n  error(...parts: unknown[]): void;\n}\n```\n\n#### overrideNativeConsole\n\nCalls overrideLogger for native console.\n\n```\noverrideNativeConsole()\n```\n\n#### catchProcessErrors\n\nSubscribes to `unhandledRejection` and `uncaughtException` events of the process to report `error` in such cases.\n\n```\ncatchProcessErrors()\n```\n\n### ServerMonitoringService\n\n#### initServerMonitoring\n\nInstantiate ServerMonitoringService with provided params and may also do additional work,\ndepending on provided variables.\n\n```\ninitServerMonitoring = (\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  monitoringOptions?: MonitoringOptions,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig\n): ServerMonitoringService\n```\n\n```\ninterface MonitoringOptions {\n  shouldOverrideNativeConsole?: boolean;\n  shouldCatchProcessErrors?: boolean;\n  globalMonitoringInstanceName?: string;\n}\n```\n\n- `RemoteMonitoringServiceParams` and `RemoteMonitoringServiceConfig` are same as in `constructor` api\n- `shouldOverrideNativeConsole` - if `true`, will call `serverMonitoringService.overrideNativeConsole()` under the hood\n- `shouldCatchProcessErrors` - if `true`, will call `serverMonitoringService.catchProcessErrors()` under the hood\n- `globalMonitoringInstanceName` - if provided with non-empty string will put instantiated `serverMonitoringService` into global scope under provided name.\n","readmeFilename":"README.md","gitHead":"8cd9c0f3c259cf5f3eb255d697aee87b5ae14448","description":"[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)","_id":"@kilohealth/web-app-monitoring@1.2.0-alpha.2","_nodeVersion":"18.16.0","_npmVersion":"9.6.7","dist":{"integrity":"sha512-uH6swlOgCCbNlivpP+IgFD5/Pf/baIfkmvqOv64DyS4ZpjSz9QePkI4GO+sbWeLercM7tL+X0wkkD9YkcaEppw==","shasum":"be0bda3b01c2aa7b3784a666f534babe46c367da","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.2.0-alpha.2.tgz","fileCount":40,"unpackedSize":54077,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHFSpGeoTXsmp6RXFL3f3Vs/DoIQRLlfyNwbejZFPuNyAiEAr5oJB4eEAN6azciIBYzKehrHaX13DxuxoM1qQj729WM="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.2.0-alpha.2_1687520328203_0.8307025072681187"},"_hasShrinkwrap":false},"1.2.0":{"name":"@kilohealth/web-app-monitoring","version":"1.2.0","license":"MIT","author":{"name":"Kilo Health"},"exports":{"./dist/server/initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./dist/server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./dist/browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","githooks:commit-msg":"commitlint --edit","githooks:pre-commit":"lint-staged","prepare":"husky install","semantic-release":"semantic-release","test":"jest","upload:sourcemaps":"./dist/cli/sourcemap-uploader.sh"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","@datadog/datadog-ci":"^2.11.0","dd-trace":"^4.0.0","deepmerge":"^4.3.1","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","@types/lodash":"^4.14.195","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jest-environment-jsdom":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"gitHead":"ed8b16009c32e38ca5713b14a3aea39f803d9efd","description":"[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)","_id":"@kilohealth/web-app-monitoring@1.2.0","_nodeVersion":"18.16.0","_npmVersion":"9.6.7","dist":{"integrity":"sha512-guruc3tFltixmSn7u/BqUVEWSBHZ1xIWwmt/1XvDfGXBrKWCEWoldXZuMRJrAind8dJUs3h6GbOp1sNXBwqnUg==","shasum":"29410b2cba16163309b713777b887d0fbdb377e3","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.2.0.tgz","fileCount":40,"unpackedSize":54069,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGqwxSMbFRjaKqeI++MSioRjR67fKxmsZl8VKmi43UsSAiB8aOPgDIasgLxF9KWaoFyiI2P10CeqELeQKyb3WKuTZg=="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.2.0_1687523017163_0.46520405955449196"},"_hasShrinkwrap":false},"1.2.1-alpha.1":{"name":"@kilohealth/web-app-monitoring","version":"1.2.1-alpha.1","license":"MIT","author":{"name":"Kilo Health"},"exports":{"./dist/server/initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./dist/server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./dist/browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","githooks:commit-msg":"commitlint --edit","githooks:pre-commit":"lint-staged","prepare":"husky install","semantic-release":"semantic-release","test":"jest","upload:sourcemaps":"./dist/cli/sourcemap-uploader.sh"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","@datadog/datadog-ci":"^2.11.0","dd-trace":"^4.0.0","deepmerge":"^4.3.1","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","@types/lodash":"^4.14.195","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jest-environment-jsdom":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"readme":"# @kilohealth/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\n## The Idea\n\nPackage was created to abstract away underlying level of monitoring,\nto make it easy to setup monitoring for any env as well as to make it easy to migrate from DataDog to other monitoring solution.\n\nThe package consists of 3 parts:\n\n- browser monitoring\n- CLI (needed to upload sourcemaps for browser monitoring)\n- server monitoring\n\n## Browser monitoring setup:\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n> If you are migrating from direct datadog integration - don’t forget to remove @datadog/browser-logs and @datadog/datadog-ci. Those are now deps of @kilohealth/web-app-monitoring.\n\n```\nnpm uninstall @datadog/browser-logs @datadog/datadog-ci\n```\n\n### Add build phase env variables\n\n- `MONITORING_TOOL__API_KEY` - this key is needed in order to uploaded sourcemaps for browser monitoring. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048).\n- `MONITORING_TOOL__SERVICE_NAME`, `MONITORING_TOOL__SERVICE_VERSION` and `MONITORING_TOOL__SERVICE_ENV`- the service name, version and env. These variables will be used by client code as well as by cli. Most client frameworks will not expose all node build phase env vars, so you probably need to reexpose them with prefix to switch on automatic replacement for client code during client build. In particular\n  - For NextJS - you have to add prefix `NEXT_PUBLIC_` to each of them. For example you have to add not only `MONITORING_TOOL__SERVICE_NAME=timely-hand-web-funnel-app` but also `NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME`. See more in [docs](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#exposing-environment-variables-to-the-browser).\n  - For GatsbyJS - you have to add prefix `GATSBY_`. See more in [docs](https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/#accessing-environment-variables-in-the-browser)\n  - For ViteJS - you have to add prefix `VITE_`. See more in [docs](https://vitejs.dev/guide/env-and-mode.html#env-files).\n- `MONITORING_TOOL__CLIENT_TOKEN` - this is client side token, which need to be built into client code in order to send logs into DD server.\n  Because it is needed on client you will have to re-expose it using same approach as variables above (probably prefixing env var).\n  Token you can create or find [here](https://app.datadoghq.com/organization-settings/client-tokens).\n  > PS: theoretically you can avoid creating `MONITORING_TOOL__CLIENT_TOKEN` env variable and create only `NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN` instead, because this variable is not needed for CLI to work. But for the sake of SRP we advocate for sticking with same reexposing approach here.\n\n#### Example of full env variables setup for client monitoring:\n\n##### Expose client token to be able to reexpose it for client-side code:\n\n```\nMONITORING_TOOL__CLIENT_TOKEN=pub2_your_client_token\n```\n\n##### Set variables for source map upload CLI to work during the build phase:\n\n```\nMONITORING_TOOL__SERVICE_ENV=$CI_ENVIRONMENT_NAME\n```\n\n```\nMONITORING_TOOL__SERVICE_NAME=greantess-funnel\n```\n\n```\nMONITORING_TOOL__SERVICE_VERSION=$CI_COMMIT_SHA\n```\n\n```\nMONITORING_TOOL__API_KEY=4be_your_api_key\n```\n\n##### Reexpose for your framework to client-side code(NextJS example):\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV=$MONITORING_TOOL__SERVICE_ENV\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION=$MONITORING_TOOL__SERVICE_VERSION\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN=$MONITORING_TOOL__CLIENT_TOKEN\n```\n\n### Modify your build code to generate sourcemaps, depending on env variable\n\nIdeally we don't want to generate and upload sourcemaps during each build.\nIn order to opt-in for this behavior sometimes we need to make additional configuration changes in our build process.\nWe need to build sourcemaps only in case specific env variable `IS_SOURCEMAP_UPLOAD_BUILD` is provided.\nWe don't provide it for dev or debug builds, only for production.\nThese are articles on how to do this for different frameworks and examples.\n\n- [Vite](https://vitejs.dev/config/build-options.html#build-sourcemap)\n\n```\nexport default defineConfig({\n  ...\n  build: {\n    sourcemap: Boolean(process.env.IS_SOURCEMAP_UPLOAD_BUILD),\n    ...\n  }\n  ...\n})\n```\n\n- [NextJS](https://nextjs.org/docs/pages/api-reference/next-config-js/productionBrowserSourceMaps)\n\n```\nmodule.exports = {\n  ...\n  productionBrowserSourceMaps: Boolean(process.env.IS_SOURCEMAP_UPLOAD_BUILD),\n  ...\n}\n```\n\n- Gatsby is a little bit more tricky. It generates sourcemaps by default. In order to prevent this you can add code to\n\n```\nmodule.exports.onCreateWebpackConfig = ({ stage, actions }) => {\n  // build-javascript is prod build phase\n  if (stage === 'build-javascript') {\n    actions.setWebpackConfig({\n      // hidden-source-map removes last line from final files,\n      // to avoid contenthash mismatch between builds\n      // we don't want sourcemaps in prod by default\n      devtool: process.env.IS_SOURCEMAP_UPLOAD_BUILD\n        ? 'hidden-source-map'\n        : false,\n    });\n  }\n};\n```\n\nThere is also an [article](https://akashrajpurohit.com/blog/disable-source-maps-in-gatsbyjs-v2/) with more details.\n\n### Add build and upload sourcemaps script to scripts section\n\n#### Prepare sourcemap upload build\n\nIt should run bin from our lib called `web-app-monitoring__upload-sourcemaps`.\nFor this script to work you would need to provide it with two variables\n\n- `MONITORING_TOOL__PUBLIC_PATH` - this is RELATIVE path, part of URL between domain (which can be different for different environments) and path to file itself.\n  In other words - base path for all the assets within your application.\n  You can think of this as kind of relative [Public Path | webpack](https://webpack.js.org/guides/public-path/).\n  For example it can be `/` or `/static`.\n  In other words this is common relative prefix for all your static files or / if there is none.\n  - for Vite default is `/`\n  - for NextJS default is `/_next/static/chunks` (!!! `_` instead of `.` in file system)\n  - for GatsbyJS default is `/`\n- `MONITORING_TOOL__BUILD_DIR` - this should be RELATIVE path to your build directory. For example `./dist` or `./build`.\n  - for Vite default is `./dist`\n  - for NextJS default is `./.next/static/chunks`\n  - for GatsbyJS default is `./public`\n\nExample for NextJS\n\n```\nMONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\n```\n\n#### 1. Approach with parallel build\n\nWe advocate for this approach.\nWith it you have separate script to build code for deployment and another one to make a build with sourcemaps to upload those to monitoring tool.\nWe decided to extract source map building and uploading into separate step because:\n\n- not each build may need these, and it will increase build time. For example, you may want to avoid this for dev builds.\n- we don’t want to manually alter the build (aka removing sourcemaps from it) because it is fragile and hard to maintain\n- we don’t want sourcemaps to leak into production, so we wanna separate generating them and uploading files into prod into different processes\n\nExample for NextJS\n\n```\n\"upload:sourcemaps\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 npm run build && MONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\"\n```\n\nAnd then your CI should run `upload:sourcemaps` script in parallel with main build,\nto avoid blocking and increasing main build time.\n\n#### 2. Approach with same build\n\nThere is alternative approach to tweak main build process with sourcemaps.\nWe need to do next things:\n\n- switch sourcemaps to be hidden.\n  For example instead of using option sourcemaps use hidden-sourcemaps.\n  With this we will avoid warning in console in production regarding the fact that files have a reference to sourcemaps but sourcemaps are not found.\n  We are just removing this reference during build phase.\n\n- extend usual build script with flag to include sourcemaps `IS_SOURCEMAP_UPLOAD_BUILD`,\n  script to upload them and script to remove them. For example\n\n```\n\"only-upload:sourcemaps\": \"MONITORING_TOOL__BUILD_DIR=./public MONITORING_TOOL__PUBLIC_PATH=/ web-app-monitoring__upload-sourcemaps\",\n\"remove:sourcemaps\": \"find ./public -name \\\"*.map\\\" -type f -delete\",\n\"build\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 gatsby build --prefix-paths && npm run only-upload:sourcemaps && npm run remove:sourcemaps\",\n```\n\n### Usage: Import and instantiate BrowserMonitoringService\n\n> Important note: there is no single entry point for package. You can't do smth like `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring';`\n> Reason for that is to avoid bundling server-code into client bundle and vice versa. This structure will ensure effective tree shaking during build time.\n\n> In case your bundler supports package.json `exports` field - you can also omit `dist` in path folder `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/browser';`\n\n```\nimport { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/dist/browser';\n\nexport const monitoring = new BrowserMonitoringService({\n  authToken: NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN,\n  serviceName: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV,\n})\n```\n\nAs you can see we are using here all our exposed variables. If any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n#### OPTIONAL: If you are using React you may benefit from utilizing [Error Boundaries](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary):\n\n```\nimport React, { Component, PropsWithChildren } from 'react';\nimport { monitoring } from '../services/monitoring';\n\ninterface ErrorBoundaryProps {}\ninterface ErrorBoundaryState {\n  hasError: boolean;\n}\n\nexport class ErrorBoundary extends Component<\n  PropsWithChildren<ErrorBoundaryProps>,\n  ErrorBoundaryState\n> {\n  static getDerivedStateFromError(_: Error): ErrorBoundaryState {\n    return { hasError: true };\n  }\n\n  state: ErrorBoundaryState = {\n    hasError: false,\n  };\n\n  componentDidCatch(error: Error) {\n    monitoring.reportError(error);\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return <h1>Sorry... There was an error</h1>;\n    }\n\n    return this.props.children;\n  }\n}\n```\n\n## Server monitoring setup (NextJS):\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n### Add build phase env variables\n\n- `MONITORING_TOOL__API_KEY` - this key is needed in order to send logs. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048).\n- `MONITORING_TOOL__SERVICE_NAME`, `MONITORING_TOOL__SERVICE_VERSION` and `MONITORING_TOOL__SERVICE_ENV` - the service name, version and env. These variables will be used to send logs.\n\n#### Example of full env variables setup for server monitoring:\n\n##### Set variables for sending logs:\n\n```\nMONITORING_TOOL__SERVICE_ENV=$CI_ENVIRONMENT_NAME\n```\n\n```\nMONITORING_TOOL__SERVICE_NAME=greantess-funnel\n```\n\n```\nMONITORING_TOOL__SERVICE_VERSION=$CI_COMMIT_SHA\n```\n\n```\nMONITORING_TOOL__API_KEY=4be_your_api_key\n```\n\n### Usage\n\n#### Approach with facade\n\n`initServerMonitoring` is a facade over `ServerMonitoringService`.\nYou can instantiate and use that service directly.\nThis function basically does one thing - instantiate it and can do 3 more additional things:\n\n- call `overrideNativeConsole` - method of the service to override native console, to log to datadog instead.\n- cal `catchProcessErrors` - method of the service to subscribe to native errors, to log them to datadog.\n- put service itself into global scope under defined name, so serverside code can use it.\n\nYou may wonder why we instantiate service here and not in server-side code.\nThe reason for that is if we override the native console and catch native errors - we would like to set up this as soon as possible.\nIf you don’t care too much about the very first seconds of next server - you can use alternative simpler server side logging solution.\n\nExample(for NextJS):\n\n- update next.config.ts to include into start script of production server code\n  `next.config.ts:`\n\n```\nconst { initServerMonitoring } = require('@kilohealth/web-app-monitoring/dist/server');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    const remoteMonitoringServiceParams = {\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    };\n    const config = {\n      shouldOverrideNativeConsole: true,\n      shouldCatchProcessErrors: true,\n      globalMonitoringInstanceName: 'kiloServerMonitoring',\n    }\n    initServerMonitoring(remoteMonitoringServiceParams, config);\n  }\n}\n```\n\n- update `custom.d.ts` file to declare that global scope now have monitoring service as a prop\n  In order to use ServerMonitoringService instance in other parts of code via global we need to let TS know\n  that we added new property to global object.\n  In NextJS you can just create or add next code into `custom.d.ts` file in root of the project.\n  Be aware that var name matches string that you provided in code above (kiloServerMonitoring in this case).\n  `custom.d.ts:`\n\n```\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\ndeclare global {\n  // eslint-disable-next-line no-var\n  var kiloServerMonitoring: ServerMonitoringService;\n}\n```\n\n- use it in code\n\n```\nexport const getHomeServerSideProps = async context => {\n  global.kiloServerMonitoring.info('getHomeServerSideProps called');\n  ...\n};\n```\n\n#### Approach with direct instantiation\n\nIf you don’t care too much about catching native errors or native logs\nin the early stages of your server app -\nyou can avoid sharing logger via global scope and instead initialize it inside of app.\n\n```\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\nexport const monitoring = new ServerMonitoringService({\n  authToken: MONITORING_TOOL__API_KEY,\n  serviceName: MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: MONITORING_TOOL__SERVICE_ENV,\n})\n```\n\nAs you can see we are using here all our env variables.\nIf any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n### Tracing setup:\n\nWe need to connect tracing as soon as possible during code, so it can be injected into all base modules for APM monitoring.\nTracing module is available via:\n\n```\nconst { initTracing } = require('@kilohealth/web-app-monitoring/dist/initTracing');\ninitTracing({\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n});\n```\n\nExample(NextJS):\n\n```\nconst { PHASE_PRODUCTION_SERVER } = require('next/constants');\nconst { initTracing } = require('@kilohealth/web-app-monitoring/dist/initTracing');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    initTracing({\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    });\n  }\n}\n```\n\n> In newer versions of NextJS there is experimental feature called\n> [instrumentationHook](https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation)\n> We can opt out from using undocumented `PHASE_PRODUCTION_SERVER` to use `instrumentationHook` for tracing init.\n\n> There is also possibility to use `NODE_OPTIONS='-r ./prestart-script.js ' next start` instead.\n> But there is an issue with `pino-datadog-transport`, which for performance reason spawns separate thread for log sending to data-dog and it this option seems to be passed to that process as well which triggers an infinite loop of require and initialization.\n\n## API\n\n### MonitoringService (both BrowserMonitoringService and ServerMonitoringService have these methods)\n\n#### debug, info, warn\n\n```\ndebug(message: string, context?: object)\ninfo(message: string, context?: object)\nwarn(message: string, context?: object)\n```\n\n- `message` - any message to be logged\n- `context` - object with all needed and related to the log entrance data\n\n#### error\n\n```\nerror(message: string, context?: object, error?: Error)\n```\n\nSame as above, but you can also optionally pass error instance as thrid parameter\n\n#### reportError\n\n```\nreportError(error: Error, context?: object)\n```\n\nShortcut for `service.error()`, which uses `error.message` field as message param for `error` method.\n\n### BrowserMonitoringService\n\n#### constructor\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```\ninterface RemoteMonitoringServiceParams {\n  serviceName?: string;\n  serviceVersion?: string;\n  serviceEnv?: string;\n  authToken?: string;\n}\n```\n\n- `RemoteMonitoringServiceConfig` - datadog params passed to init function. More info in [docs](https://docs.datadoghq.com/logs/log_collection/javascript/#initialization-parameters)\n- `serviceName` - name of the service\n- `serviceVersion` - version of the service\n- `serviceEnv` - environment where service is deployed\n- `authToken` - client token\n\n### ServerMonitoringService\n\n#### constructor\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```\ninterface RemoteMonitoringServiceConfig {\n  transportOptions?: Partial<TransportBaseOptions>;\n  loggerOptions?: Partial<LoggerOptions>;\n}\n```\n\n- `transportOptions` - [pino-datadog-transport options](https://github.com/theogravity/pino-datadog-transport#configuration-options)\n- `loggerOptions` - [pino logger options](https://getpino.io/#/docs/api?id=options-object)\n\n#### overrideLogger\n\nOverrides logger passed as argument with monitoring logger.\nAll methods of this logger will be overridden with corresponding methods of server monitoring.\n\n```\noverrideLogger(unknownLogger: UnknownLogger)\n```\n\n```\ninterface UnknownLogger {\n  log?(...parts: unknown[]): void;\n  debug?(...parts: unknown[]): void;\n  info(...parts: unknown[]): void;\n  warn(...parts: unknown[]): void;\n  error(...parts: unknown[]): void;\n}\n```\n\n#### overrideNativeConsole\n\nCalls overrideLogger for native console.\n\n```\noverrideNativeConsole()\n```\n\n#### catchProcessErrors\n\nSubscribes to `unhandledRejection` and `uncaughtException` events of the process to report `error` in such cases.\n\n```\ncatchProcessErrors()\n```\n\n### ServerMonitoringService\n\n#### initServerMonitoring\n\nInstantiate ServerMonitoringService with provided params and may also do additional work,\ndepending on provided variables.\n\n```\ninitServerMonitoring = (\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  monitoringOptions?: MonitoringOptions,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig\n): ServerMonitoringService\n```\n\n```\ninterface MonitoringOptions {\n  shouldOverrideNativeConsole?: boolean;\n  shouldCatchProcessErrors?: boolean;\n  globalMonitoringInstanceName?: string;\n}\n```\n\n- `RemoteMonitoringServiceParams` and `RemoteMonitoringServiceConfig` are same as in `constructor` api\n- `shouldOverrideNativeConsole` - if `true`, will call `serverMonitoringService.overrideNativeConsole()` under the hood\n- `shouldCatchProcessErrors` - if `true`, will call `serverMonitoringService.catchProcessErrors()` under the hood\n- `globalMonitoringInstanceName` - if provided with non-empty string will put instantiated `serverMonitoringService` into global scope under provided name.\n","readmeFilename":"README.md","gitHead":"9bdcd0c3ebdb7af9d8b61f4d7aa7a0a6115aae17","description":"[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)","_id":"@kilohealth/web-app-monitoring@1.2.1-alpha.1","_nodeVersion":"18.16.1","_npmVersion":"9.6.7","dist":{"integrity":"sha512-bxUEIiZZU1RE6RdYBot0p+WOeNjwVm9ZFy6qThjerW9cWE+/Ol3ZaMzQhfPeIEEjhIH/A1Lcw5mkcal2GUggjg==","shasum":"7d33bbedfa08db443bd27ecbac234d2832154bc8","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.2.1-alpha.1.tgz","fileCount":40,"unpackedSize":54077,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD5TzjggUd9c2+m608aaipuh9rRfrn/zcqeY5idxkQ5hgIgaMcVIOzLn3PnUU33SQ7L+RAYhF1HbVBjjQMF9vA/DLc="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.2.1-alpha.1_1688131924872_0.9778720100367746"},"_hasShrinkwrap":false},"1.2.1-alpha.2":{"name":"@kilohealth/web-app-monitoring","version":"1.2.1-alpha.2","license":"MIT","author":{"name":"Kilo Health"},"exports":{"./dist/server/initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./dist/server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./dist/browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"bin":{"datadog-ci":"datadog-ci","web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","githooks:commit-msg":"commitlint --edit","githooks:pre-commit":"lint-staged","prepare":"husky install","semantic-release":"semantic-release","test":"jest","upload:sourcemaps":"./dist/cli/sourcemap-uploader.sh"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","@datadog/datadog-ci":"^2.11.0","dd-trace":"^4.0.0","deepmerge":"^4.3.1","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","@types/lodash":"^4.14.195","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jest-environment-jsdom":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"readme":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)\n\n# @kilohealth/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\n## The Idea\n\nPackage was created to abstract away underlying level of monitoring,\nto make it easy to setup monitoring for any env as well as to make it easy to migrate from DataDog to other monitoring solution.\n\nThe package consists of 3 parts:\n\n- browser monitoring\n- CLI (needed to upload sourcemaps for browser monitoring)\n- server monitoring\n\n## Browser monitoring setup:\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n> If you are migrating from direct datadog integration - don’t forget to remove @datadog/browser-logs and @datadog/datadog-ci. Those are now deps of @kilohealth/web-app-monitoring.\n\n```\nnpm uninstall @datadog/browser-logs @datadog/datadog-ci\n```\n\n### Add build phase env variables\n\n- `MONITORING_TOOL__API_KEY` - this key is needed in order to uploaded sourcemaps for browser monitoring. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048).\n- `MONITORING_TOOL__SERVICE_NAME`, `MONITORING_TOOL__SERVICE_VERSION` and `MONITORING_TOOL__SERVICE_ENV`- the service name, version and env. These variables will be used by client code as well as by cli. Most client frameworks will not expose all node build phase env vars, so you probably need to reexpose them with prefix to switch on automatic replacement for client code during client build. In particular\n  - For NextJS - you have to add prefix `NEXT_PUBLIC_` to each of them. For example you have to add not only `MONITORING_TOOL__SERVICE_NAME=timely-hand-web-funnel-app` but also `NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME`. See more in [docs](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#exposing-environment-variables-to-the-browser).\n  - For GatsbyJS - you have to add prefix `GATSBY_`. See more in [docs](https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/#accessing-environment-variables-in-the-browser)\n  - For ViteJS - you have to add prefix `VITE_`. See more in [docs](https://vitejs.dev/guide/env-and-mode.html#env-files).\n- `MONITORING_TOOL__CLIENT_TOKEN` - this is client side token, which need to be built into client code in order to send logs into DD server.\n  Because it is needed on client you will have to re-expose it using same approach as variables above (probably prefixing env var).\n  Token you can create or find [here](https://app.datadoghq.com/organization-settings/client-tokens).\n  > PS: theoretically you can avoid creating `MONITORING_TOOL__CLIENT_TOKEN` env variable and create only `NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN` instead, because this variable is not needed for CLI to work. But for the sake of SRP we advocate for sticking with same reexposing approach here.\n\n#### Example of full env variables setup for client monitoring:\n\n##### Expose client token to be able to reexpose it for client-side code:\n\n```\nMONITORING_TOOL__CLIENT_TOKEN=pub2_your_client_token\n```\n\n##### Set variables for source map upload CLI to work during the build phase:\n\n```\nMONITORING_TOOL__SERVICE_ENV=$CI_ENVIRONMENT_NAME\n```\n\n```\nMONITORING_TOOL__SERVICE_NAME=greantess-funnel\n```\n\n```\nMONITORING_TOOL__SERVICE_VERSION=$CI_COMMIT_SHA\n```\n\n```\nMONITORING_TOOL__API_KEY=4be_your_api_key\n```\n\n##### Reexpose for your framework to client-side code(NextJS example):\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV=$MONITORING_TOOL__SERVICE_ENV\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION=$MONITORING_TOOL__SERVICE_VERSION\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN=$MONITORING_TOOL__CLIENT_TOKEN\n```\n\n### Modify your build code to generate sourcemaps, depending on env variable\n\nIdeally we don't want to generate and upload sourcemaps during each build.\nIn order to opt-in for this behavior sometimes we need to make additional configuration changes in our build process.\nWe need to build sourcemaps only in case specific env variable `IS_SOURCEMAP_UPLOAD_BUILD` is provided.\nWe don't provide it for dev or debug builds, only for production.\nThese are articles on how to do this for different frameworks and examples.\n\n- [Vite](https://vitejs.dev/config/build-options.html#build-sourcemap)\n\n```\nexport default defineConfig({\n  ...\n  build: {\n    sourcemap: Boolean(process.env.IS_SOURCEMAP_UPLOAD_BUILD),\n    ...\n  }\n  ...\n})\n```\n\n- [NextJS](https://nextjs.org/docs/pages/api-reference/next-config-js/productionBrowserSourceMaps)\n\n```\nmodule.exports = {\n  ...\n  productionBrowserSourceMaps: Boolean(process.env.IS_SOURCEMAP_UPLOAD_BUILD),\n  ...\n}\n```\n\n- Gatsby is a little bit more tricky. It generates sourcemaps by default. In order to prevent this you can add code to\n\n```\nmodule.exports.onCreateWebpackConfig = ({ stage, actions }) => {\n  // build-javascript is prod build phase\n  if (stage === 'build-javascript') {\n    actions.setWebpackConfig({\n      // hidden-source-map removes last line from final files,\n      // to avoid contenthash mismatch between builds\n      // we don't want sourcemaps in prod by default\n      devtool: process.env.IS_SOURCEMAP_UPLOAD_BUILD\n        ? 'hidden-source-map'\n        : false,\n    });\n  }\n};\n```\n\nThere is also an [article](https://akashrajpurohit.com/blog/disable-source-maps-in-gatsbyjs-v2/) with more details.\n\n### Add build and upload sourcemaps script to scripts section\n\n#### Prepare sourcemap upload build\n\nIt should run bin from our lib called `web-app-monitoring__upload-sourcemaps`.\nFor this script to work you would need to provide it with two variables\n\n- `MONITORING_TOOL__PUBLIC_PATH` - this is RELATIVE path, part of URL between domain (which can be different for different environments) and path to file itself.\n  In other words - base path for all the assets within your application.\n  You can think of this as kind of relative [Public Path | webpack](https://webpack.js.org/guides/public-path/).\n  For example it can be `/` or `/static`.\n  In other words this is common relative prefix for all your static files or / if there is none.\n  - for Vite default is `/`\n  - for NextJS default is `/_next/static/chunks` (!!! `_` instead of `.` in file system)\n  - for GatsbyJS default is `/`\n- `MONITORING_TOOL__BUILD_DIR` - this should be RELATIVE path to your build directory. For example `./dist` or `./build`.\n  - for Vite default is `./dist`\n  - for NextJS default is `./.next/static/chunks`\n  - for GatsbyJS default is `./public`\n\nExample for NextJS\n\n```\nMONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\n```\n\n#### 1. Approach with parallel build\n\nWe advocate for this approach.\nWith it you have separate script to build code for deployment and another one to make a build with sourcemaps to upload those to monitoring tool.\nWe decided to extract source map building and uploading into separate step because:\n\n- not each build may need these, and it will increase build time. For example, you may want to avoid this for dev builds.\n- we don’t want to manually alter the build (aka removing sourcemaps from it) because it is fragile and hard to maintain\n- we don’t want sourcemaps to leak into production, so we wanna separate generating them and uploading files into prod into different processes\n\nExample for NextJS\n\n```\n\"upload:sourcemaps\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 npm run build && MONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\"\n```\n\nAnd then your CI should run `upload:sourcemaps` script in parallel with main build,\nto avoid blocking and increasing main build time.\n\n#### 2. Approach with same build\n\nThere is alternative approach to tweak main build process with sourcemaps.\nWe need to do next things:\n\n- switch sourcemaps to be hidden.\n  For example instead of using option sourcemaps use hidden-sourcemaps.\n  With this we will avoid warning in console in production regarding the fact that files have a reference to sourcemaps but sourcemaps are not found.\n  We are just removing this reference during build phase.\n\n- extend usual build script with flag to include sourcemaps `IS_SOURCEMAP_UPLOAD_BUILD`,\n  script to upload them and script to remove them. For example\n\n```\n\"only-upload:sourcemaps\": \"MONITORING_TOOL__BUILD_DIR=./public MONITORING_TOOL__PUBLIC_PATH=/ web-app-monitoring__upload-sourcemaps\",\n\"remove:sourcemaps\": \"find ./public -name \\\"*.map\\\" -type f -delete\",\n\"build\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 gatsby build --prefix-paths && npm run only-upload:sourcemaps && npm run remove:sourcemaps\",\n```\n\n### Usage: Import and instantiate BrowserMonitoringService\n\n> Important note: there is no single entry point for package. You can't do smth like `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring';`\n> Reason for that is to avoid bundling server-code into client bundle and vice versa. This structure will ensure effective tree shaking during build time.\n\n> In case your bundler supports package.json `exports` field - you can also omit `dist` in path folder `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/browser';`\n\n```\nimport { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/dist/browser';\n\nexport const monitoring = new BrowserMonitoringService({\n  authToken: NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN,\n  serviceName: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV,\n})\n```\n\nAs you can see we are using here all our exposed variables. If any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n#### OPTIONAL: If you are using React you may benefit from utilizing [Error Boundaries](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary):\n\n```\nimport React, { Component, PropsWithChildren } from 'react';\nimport { monitoring } from '../services/monitoring';\n\ninterface ErrorBoundaryProps {}\ninterface ErrorBoundaryState {\n  hasError: boolean;\n}\n\nexport class ErrorBoundary extends Component<\n  PropsWithChildren<ErrorBoundaryProps>,\n  ErrorBoundaryState\n> {\n  static getDerivedStateFromError(_: Error): ErrorBoundaryState {\n    return { hasError: true };\n  }\n\n  state: ErrorBoundaryState = {\n    hasError: false,\n  };\n\n  componentDidCatch(error: Error) {\n    monitoring.reportError(error);\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return <h1>Sorry... There was an error</h1>;\n    }\n\n    return this.props.children;\n  }\n}\n```\n\n## Server monitoring setup (NextJS):\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n### Add build phase env variables\n\n- `MONITORING_TOOL__API_KEY` - this key is needed in order to send logs. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048).\n- `MONITORING_TOOL__SERVICE_NAME`, `MONITORING_TOOL__SERVICE_VERSION` and `MONITORING_TOOL__SERVICE_ENV` - the service name, version and env. These variables will be used to send logs.\n\n#### Example of full env variables setup for server monitoring:\n\n##### Set variables for sending logs:\n\n```\nMONITORING_TOOL__SERVICE_ENV=$CI_ENVIRONMENT_NAME\n```\n\n```\nMONITORING_TOOL__SERVICE_NAME=greantess-funnel\n```\n\n```\nMONITORING_TOOL__SERVICE_VERSION=$CI_COMMIT_SHA\n```\n\n```\nMONITORING_TOOL__API_KEY=4be_your_api_key\n```\n\n### Usage\n\n#### Approach with facade\n\n`initServerMonitoring` is a facade over `ServerMonitoringService`.\nYou can instantiate and use that service directly.\nThis function basically does one thing - instantiate it and can do 3 more additional things:\n\n- call `overrideNativeConsole` - method of the service to override native console, to log to datadog instead.\n- cal `catchProcessErrors` - method of the service to subscribe to native errors, to log them to datadog.\n- put service itself into global scope under defined name, so serverside code can use it.\n\nYou may wonder why we instantiate service here and not in server-side code.\nThe reason for that is if we override the native console and catch native errors - we would like to set up this as soon as possible.\nIf you don’t care too much about the very first seconds of next server - you can use alternative simpler server side logging solution.\n\nExample(for NextJS):\n\n- update next.config.ts to include into start script of production server code\n  `next.config.ts:`\n\n```\nconst { initServerMonitoring } = require('@kilohealth/web-app-monitoring/dist/server');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    const remoteMonitoringServiceParams = {\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    };\n    const config = {\n      shouldOverrideNativeConsole: true,\n      shouldCatchProcessErrors: true,\n      globalMonitoringInstanceName: 'kiloServerMonitoring',\n    }\n    initServerMonitoring(remoteMonitoringServiceParams, config);\n  }\n}\n```\n\n- update `custom.d.ts` file to declare that global scope now have monitoring service as a prop\n  In order to use ServerMonitoringService instance in other parts of code via global we need to let TS know\n  that we added new property to global object.\n  In NextJS you can just create or add next code into `custom.d.ts` file in root of the project.\n  Be aware that var name matches string that you provided in code above (kiloServerMonitoring in this case).\n  `custom.d.ts:`\n\n```\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\ndeclare global {\n  // eslint-disable-next-line no-var\n  var kiloServerMonitoring: ServerMonitoringService;\n}\n```\n\n- use it in code\n\n```\nexport const getHomeServerSideProps = async context => {\n  global.kiloServerMonitoring.info('getHomeServerSideProps called');\n  ...\n};\n```\n\n#### Approach with direct instantiation\n\nIf you don’t care too much about catching native errors or native logs\nin the early stages of your server app -\nyou can avoid sharing logger via global scope and instead initialize it inside of app.\n\n```\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\nexport const monitoring = new ServerMonitoringService({\n  authToken: MONITORING_TOOL__API_KEY,\n  serviceName: MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: MONITORING_TOOL__SERVICE_ENV,\n})\n```\n\nAs you can see we are using here all our env variables.\nIf any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n### Tracing setup:\n\nWe need to connect tracing as soon as possible during code, so it can be injected into all base modules for APM monitoring.\nTracing module is available via:\n\n```\nconst { initTracing } = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\ninitTracing({\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n});\n```\n\nExample(NextJS):\n\n```\nconst { PHASE_PRODUCTION_SERVER } = require('next/constants');\nconst { initTracing } = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    initTracing({\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    });\n  }\n}\n```\n\n> In newer versions of NextJS there is experimental feature called\n> [instrumentationHook](https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation)\n> We can opt out from using undocumented `PHASE_PRODUCTION_SERVER` to use `instrumentationHook` for tracing init.\n\n> There is also possibility to use `NODE_OPTIONS='-r ./prestart-script.js ' next start` instead.\n> But there is an issue with `pino-datadog-transport`, which for performance reason spawns separate thread for log sending to data-dog and it this option seems to be passed to that process as well which triggers an infinite loop of require and initialization.\n\n## API\n\n### MonitoringService (both BrowserMonitoringService and ServerMonitoringService have these methods)\n\n#### debug, info, warn\n\n```\ndebug(message: string, context?: object)\ninfo(message: string, context?: object)\nwarn(message: string, context?: object)\n```\n\n- `message` - any message to be logged\n- `context` - object with all needed and related to the log entrance data\n\n#### error\n\n```\nerror(message: string, context?: object, error?: Error)\n```\n\nSame as above, but you can also optionally pass error instance as thrid parameter\n\n#### reportError\n\n```\nreportError(error: Error, context?: object)\n```\n\nShortcut for `service.error()`, which uses `error.message` field as message param for `error` method.\n\n### BrowserMonitoringService\n\n#### constructor\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```\ninterface RemoteMonitoringServiceParams {\n  serviceName?: string;\n  serviceVersion?: string;\n  serviceEnv?: string;\n  authToken?: string;\n}\n```\n\n- `RemoteMonitoringServiceConfig` - datadog params passed to init function. More info in [docs](https://docs.datadoghq.com/logs/log_collection/javascript/#initialization-parameters)\n- `serviceName` - name of the service\n- `serviceVersion` - version of the service\n- `serviceEnv` - environment where service is deployed\n- `authToken` - client token\n\n### ServerMonitoringService\n\n#### constructor\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```\ninterface RemoteMonitoringServiceConfig {\n  transportOptions?: Partial<TransportBaseOptions>;\n  loggerOptions?: Partial<LoggerOptions>;\n}\n```\n\n- `transportOptions` - [pino-datadog-transport options](https://github.com/theogravity/pino-datadog-transport#configuration-options)\n- `loggerOptions` - [pino logger options](https://getpino.io/#/docs/api?id=options-object)\n\n#### overrideLogger\n\nOverrides logger passed as argument with monitoring logger.\nAll methods of this logger will be overridden with corresponding methods of server monitoring.\n\n```\noverrideLogger(unknownLogger: UnknownLogger)\n```\n\n```\ninterface UnknownLogger {\n  log?(...parts: unknown[]): void;\n  debug?(...parts: unknown[]): void;\n  info(...parts: unknown[]): void;\n  warn(...parts: unknown[]): void;\n  error(...parts: unknown[]): void;\n}\n```\n\n#### overrideNativeConsole\n\nCalls overrideLogger for native console.\n\n```\noverrideNativeConsole()\n```\n\n#### catchProcessErrors\n\nSubscribes to `unhandledRejection` and `uncaughtException` events of the process to report `error` in such cases.\n\n```\ncatchProcessErrors()\n```\n\n### ServerMonitoringService\n\n#### initServerMonitoring\n\nInstantiate ServerMonitoringService with provided params and may also do additional work,\ndepending on provided variables.\n\n```\ninitServerMonitoring = (\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  monitoringOptions?: MonitoringOptions,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig\n): ServerMonitoringService\n```\n\n```\ninterface MonitoringOptions {\n  shouldOverrideNativeConsole?: boolean;\n  shouldCatchProcessErrors?: boolean;\n  globalMonitoringInstanceName?: string;\n}\n```\n\n- `RemoteMonitoringServiceParams` and `RemoteMonitoringServiceConfig` are same as in `constructor` api\n- `shouldOverrideNativeConsole` - if `true`, will call `serverMonitoringService.overrideNativeConsole()` under the hood\n- `shouldCatchProcessErrors` - if `true`, will call `serverMonitoringService.catchProcessErrors()` under the hood\n- `globalMonitoringInstanceName` - if provided with non-empty string will put instantiated `serverMonitoringService` into global scope under provided name.\n","readmeFilename":"README.md","gitHead":"ac4b31370b782e1ad5267831080554e94a826b9f","description":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)","_id":"@kilohealth/web-app-monitoring@1.2.1-alpha.2","_nodeVersion":"18.16.1","_npmVersion":"9.6.7","dist":{"integrity":"sha512-3RTaMprCsO1UaRSGWIt0QRjecYY6NlLSdR2aR7NX3OJ1idPZY/u4mk3BHQtuUQFpIG9DuN0DiCjjYCrXPYu82w==","shasum":"1486af24b9c704eb006c5018d10535c9c8973421","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.2.1-alpha.2.tgz","fileCount":40,"unpackedSize":54264,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAsrOaHG4QPfoCgY8eJSTcyDMK8OgiDgH2ZLGCvkF1xyAiBWdh1KRFCyQQAX20ymcueVwmrqJqyfYRoAhiPX5SqG9w=="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.2.1-alpha.2_1688146925003_0.7099606754615675"},"_hasShrinkwrap":false},"1.2.1-alpha.3":{"name":"@kilohealth/web-app-monitoring","version":"1.2.1-alpha.3","license":"MIT","author":{"name":"Kilo Health"},"exports":{"./dist/server/initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./dist/server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./dist/browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","githooks:commit-msg":"commitlint --edit","githooks:pre-commit":"lint-staged","prepare":"husky install","semantic-release":"semantic-release","test":"jest"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","dd-trace":"^4.0.0","deepmerge":"^4.3.1","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","@types/lodash":"^4.14.195","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jest-environment-jsdom":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"readme":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)\n\n# @kilohealth/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\n## The Idea\n\nPackage was created to abstract away underlying level of monitoring,\nto make it easy to setup monitoring for any env as well as to make it easy to migrate from DataDog to other monitoring solution.\n\nThe package consists of 3 parts:\n\n- browser monitoring\n- CLI (needed to upload sourcemaps for browser monitoring)\n- server monitoring\n\n## Browser monitoring setup:\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n> If you are migrating from direct datadog integration - don’t forget to remove @datadog/browser-logs and @datadog/datadog-ci. Those are now deps of @kilohealth/web-app-monitoring.\n\n```\nnpm uninstall @datadog/browser-logs @datadog/datadog-ci\n```\n\n### Add build phase env variables\n\n- `MONITORING_TOOL__API_KEY` - this key is needed in order to uploaded sourcemaps for browser monitoring. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048).\n- `MONITORING_TOOL__SERVICE_NAME`, `MONITORING_TOOL__SERVICE_VERSION` and `MONITORING_TOOL__SERVICE_ENV`- the service name, version and env. These variables will be used by client code as well as by cli. Most client frameworks will not expose all node build phase env vars, so you probably need to reexpose them with prefix to switch on automatic replacement for client code during client build. In particular\n  - For NextJS - you have to add prefix `NEXT_PUBLIC_` to each of them. For example you have to add not only `MONITORING_TOOL__SERVICE_NAME=timely-hand-web-funnel-app` but also `NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME`. See more in [docs](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#exposing-environment-variables-to-the-browser).\n  - For GatsbyJS - you have to add prefix `GATSBY_`. See more in [docs](https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/#accessing-environment-variables-in-the-browser)\n  - For ViteJS - you have to add prefix `VITE_`. See more in [docs](https://vitejs.dev/guide/env-and-mode.html#env-files).\n- `MONITORING_TOOL__CLIENT_TOKEN` - this is client side token, which need to be built into client code in order to send logs into DD server.\n  Because it is needed on client you will have to re-expose it using same approach as variables above (probably prefixing env var).\n  Token you can create or find [here](https://app.datadoghq.com/organization-settings/client-tokens).\n  > PS: theoretically you can avoid creating `MONITORING_TOOL__CLIENT_TOKEN` env variable and create only `NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN` instead, because this variable is not needed for CLI to work. But for the sake of SRP we advocate for sticking with same reexposing approach here.\n\n#### Example of full env variables setup for client monitoring:\n\n##### Expose client token to be able to reexpose it for client-side code:\n\n```\nMONITORING_TOOL__CLIENT_TOKEN=pub2_your_client_token\n```\n\n##### Set variables for source map upload CLI to work during the build phase:\n\n```\nMONITORING_TOOL__SERVICE_ENV=$CI_ENVIRONMENT_NAME\n```\n\n```\nMONITORING_TOOL__SERVICE_NAME=greantess-funnel\n```\n\n```\nMONITORING_TOOL__SERVICE_VERSION=$CI_COMMIT_SHA\n```\n\n```\nMONITORING_TOOL__API_KEY=4be_your_api_key\n```\n\n##### Reexpose for your framework to client-side code(NextJS example):\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV=$MONITORING_TOOL__SERVICE_ENV\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION=$MONITORING_TOOL__SERVICE_VERSION\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN=$MONITORING_TOOL__CLIENT_TOKEN\n```\n\n### Modify your build code to generate sourcemaps, depending on env variable\n\nIdeally we don't want to generate and upload sourcemaps during each build.\nIn order to opt-in for this behavior sometimes we need to make additional configuration changes in our build process.\nWe need to build sourcemaps only in case specific env variable `IS_SOURCEMAP_UPLOAD_BUILD` is provided.\nWe don't provide it for dev or debug builds, only for production.\nThese are articles on how to do this for different frameworks and examples.\n\n- [Vite](https://vitejs.dev/config/build-options.html#build-sourcemap)\n\n```\nexport default defineConfig({\n  ...\n  build: {\n    sourcemap: Boolean(process.env.IS_SOURCEMAP_UPLOAD_BUILD),\n    ...\n  }\n  ...\n})\n```\n\n- [NextJS](https://nextjs.org/docs/pages/api-reference/next-config-js/productionBrowserSourceMaps)\n\n```\nmodule.exports = {\n  ...\n  productionBrowserSourceMaps: Boolean(process.env.IS_SOURCEMAP_UPLOAD_BUILD),\n  ...\n}\n```\n\n- Gatsby is a little bit more tricky. It generates sourcemaps by default. In order to prevent this you can add code to\n\n```\nmodule.exports.onCreateWebpackConfig = ({ stage, actions }) => {\n  // build-javascript is prod build phase\n  if (stage === 'build-javascript') {\n    actions.setWebpackConfig({\n      // hidden-source-map removes last line from final files,\n      // to avoid contenthash mismatch between builds\n      // we don't want sourcemaps in prod by default\n      devtool: process.env.IS_SOURCEMAP_UPLOAD_BUILD\n        ? 'hidden-source-map'\n        : false,\n    });\n  }\n};\n```\n\nThere is also an [article](https://akashrajpurohit.com/blog/disable-source-maps-in-gatsbyjs-v2/) with more details.\n\n### Add build and upload sourcemaps script to scripts section\n\n#### Prepare sourcemap upload build\n\nIt should run bin from our lib called `web-app-monitoring__upload-sourcemaps`.\nFor this script to work you would need to provide it with two variables\n\n- `MONITORING_TOOL__PUBLIC_PATH` - this is RELATIVE path, part of URL between domain (which can be different for different environments) and path to file itself.\n  In other words - base path for all the assets within your application.\n  You can think of this as kind of relative [Public Path | webpack](https://webpack.js.org/guides/public-path/).\n  For example it can be `/` or `/static`.\n  In other words this is common relative prefix for all your static files or / if there is none.\n  - for Vite default is `/`\n  - for NextJS default is `/_next/static/chunks` (!!! `_` instead of `.` in file system)\n  - for GatsbyJS default is `/`\n- `MONITORING_TOOL__BUILD_DIR` - this should be RELATIVE path to your build directory. For example `./dist` or `./build`.\n  - for Vite default is `./dist`\n  - for NextJS default is `./.next/static/chunks`\n  - for GatsbyJS default is `./public`\n\nExample for NextJS\n\n```\nMONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\n```\n\n#### 1. Approach with parallel build\n\nWe advocate for this approach.\nWith it you have separate script to build code for deployment and another one to make a build with sourcemaps to upload those to monitoring tool.\nWe decided to extract source map building and uploading into separate step because:\n\n- not each build may need these, and it will increase build time. For example, you may want to avoid this for dev builds.\n- we don’t want to manually alter the build (aka removing sourcemaps from it) because it is fragile and hard to maintain\n- we don’t want sourcemaps to leak into production, so we wanna separate generating them and uploading files into prod into different processes\n\nExample for NextJS\n\n```\n\"upload:sourcemaps\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 npm run build && MONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\"\n```\n\nAnd then your CI should run `upload:sourcemaps` script in parallel with main build,\nto avoid blocking and increasing main build time.\n\n#### 2. Approach with same build\n\nThere is alternative approach to tweak main build process with sourcemaps.\nWe need to do next things:\n\n- switch sourcemaps to be hidden.\n  For example instead of using option sourcemaps use hidden-sourcemaps.\n  With this we will avoid warning in console in production regarding the fact that files have a reference to sourcemaps but sourcemaps are not found.\n  We are just removing this reference during build phase.\n\n- extend usual build script with flag to include sourcemaps `IS_SOURCEMAP_UPLOAD_BUILD`,\n  script to upload them and script to remove them. For example\n\n```\n\"only-upload:sourcemaps\": \"MONITORING_TOOL__BUILD_DIR=./public MONITORING_TOOL__PUBLIC_PATH=/ web-app-monitoring__upload-sourcemaps\",\n\"remove:sourcemaps\": \"find ./public -name \\\"*.map\\\" -type f -delete\",\n\"build\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 gatsby build --prefix-paths && npm run only-upload:sourcemaps && npm run remove:sourcemaps\",\n```\n\n### Usage: Import and instantiate BrowserMonitoringService\n\n> Important note: there is no single entry point for package. You can't do smth like `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring';`\n> Reason for that is to avoid bundling server-code into client bundle and vice versa. This structure will ensure effective tree shaking during build time.\n\n> In case your bundler supports package.json `exports` field - you can also omit `dist` in path folder `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/browser';`\n\n```\nimport { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/dist/browser';\n\nexport const monitoring = new BrowserMonitoringService({\n  authToken: NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN,\n  serviceName: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV,\n})\n```\n\nAs you can see we are using here all our exposed variables. If any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n#### OPTIONAL: If you are using React you may benefit from utilizing [Error Boundaries](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary):\n\n```\nimport React, { Component, PropsWithChildren } from 'react';\nimport { monitoring } from '../services/monitoring';\n\ninterface ErrorBoundaryProps {}\ninterface ErrorBoundaryState {\n  hasError: boolean;\n}\n\nexport class ErrorBoundary extends Component<\n  PropsWithChildren<ErrorBoundaryProps>,\n  ErrorBoundaryState\n> {\n  static getDerivedStateFromError(_: Error): ErrorBoundaryState {\n    return { hasError: true };\n  }\n\n  state: ErrorBoundaryState = {\n    hasError: false,\n  };\n\n  componentDidCatch(error: Error) {\n    monitoring.reportError(error);\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return <h1>Sorry... There was an error</h1>;\n    }\n\n    return this.props.children;\n  }\n}\n```\n\n## Server monitoring setup (NextJS):\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n### Add build phase env variables\n\n- `MONITORING_TOOL__API_KEY` - this key is needed in order to send logs. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048).\n- `MONITORING_TOOL__SERVICE_NAME`, `MONITORING_TOOL__SERVICE_VERSION` and `MONITORING_TOOL__SERVICE_ENV` - the service name, version and env. These variables will be used to send logs.\n\n#### Example of full env variables setup for server monitoring:\n\n##### Set variables for sending logs:\n\n```\nMONITORING_TOOL__SERVICE_ENV=$CI_ENVIRONMENT_NAME\n```\n\n```\nMONITORING_TOOL__SERVICE_NAME=greantess-funnel\n```\n\n```\nMONITORING_TOOL__SERVICE_VERSION=$CI_COMMIT_SHA\n```\n\n```\nMONITORING_TOOL__API_KEY=4be_your_api_key\n```\n\n### Usage\n\n#### Approach with facade\n\n`initServerMonitoring` is a facade over `ServerMonitoringService`.\nYou can instantiate and use that service directly.\nThis function basically does one thing - instantiate it and can do 3 more additional things:\n\n- call `overrideNativeConsole` - method of the service to override native console, to log to datadog instead.\n- cal `catchProcessErrors` - method of the service to subscribe to native errors, to log them to datadog.\n- put service itself into global scope under defined name, so serverside code can use it.\n\nYou may wonder why we instantiate service here and not in server-side code.\nThe reason for that is if we override the native console and catch native errors - we would like to set up this as soon as possible.\nIf you don’t care too much about the very first seconds of next server - you can use alternative simpler server side logging solution.\n\nExample(for NextJS):\n\n- update next.config.ts to include into start script of production server code\n  `next.config.ts:`\n\n```\nconst { initServerMonitoring } = require('@kilohealth/web-app-monitoring/dist/server');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    const remoteMonitoringServiceParams = {\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    };\n    const config = {\n      shouldOverrideNativeConsole: true,\n      shouldCatchProcessErrors: true,\n      globalMonitoringInstanceName: 'kiloServerMonitoring',\n    }\n    initServerMonitoring(remoteMonitoringServiceParams, config);\n  }\n}\n```\n\n- update `custom.d.ts` file to declare that global scope now have monitoring service as a prop\n  In order to use ServerMonitoringService instance in other parts of code via global we need to let TS know\n  that we added new property to global object.\n  In NextJS you can just create or add next code into `custom.d.ts` file in root of the project.\n  Be aware that var name matches string that you provided in code above (kiloServerMonitoring in this case).\n  `custom.d.ts:`\n\n```\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\ndeclare global {\n  // eslint-disable-next-line no-var\n  var kiloServerMonitoring: ServerMonitoringService;\n}\n```\n\n- use it in code\n\n```\nexport const getHomeServerSideProps = async context => {\n  global.kiloServerMonitoring.info('getHomeServerSideProps called');\n  ...\n};\n```\n\n#### Approach with direct instantiation\n\nIf you don’t care too much about catching native errors or native logs\nin the early stages of your server app -\nyou can avoid sharing logger via global scope and instead initialize it inside of app.\n\n```\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\nexport const monitoring = new ServerMonitoringService({\n  authToken: MONITORING_TOOL__API_KEY,\n  serviceName: MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: MONITORING_TOOL__SERVICE_ENV,\n})\n```\n\nAs you can see we are using here all our env variables.\nIf any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n### Tracing setup:\n\nWe need to connect tracing as soon as possible during code, so it can be injected into all base modules for APM monitoring.\nTracing module is available via:\n\n```\nconst { initTracing } = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\ninitTracing({\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n});\n```\n\nExample(NextJS):\n\n```\nconst { PHASE_PRODUCTION_SERVER } = require('next/constants');\nconst { initTracing } = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    initTracing({\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    });\n  }\n}\n```\n\n> In newer versions of NextJS there is experimental feature called\n> [instrumentationHook](https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation)\n> We can opt out from using undocumented `PHASE_PRODUCTION_SERVER` to use `instrumentationHook` for tracing init.\n\n> There is also possibility to use `NODE_OPTIONS='-r ./prestart-script.js ' next start` instead.\n> But there is an issue with `pino-datadog-transport`, which for performance reason spawns separate thread for log sending to data-dog and it this option seems to be passed to that process as well which triggers an infinite loop of require and initialization.\n\n## API\n\n### MonitoringService (both BrowserMonitoringService and ServerMonitoringService have these methods)\n\n#### debug, info, warn\n\n```\ndebug(message: string, context?: object)\ninfo(message: string, context?: object)\nwarn(message: string, context?: object)\n```\n\n- `message` - any message to be logged\n- `context` - object with all needed and related to the log entrance data\n\n#### error\n\n```\nerror(message: string, context?: object, error?: Error)\n```\n\nSame as above, but you can also optionally pass error instance as thrid parameter\n\n#### reportError\n\n```\nreportError(error: Error, context?: object)\n```\n\nShortcut for `service.error()`, which uses `error.message` field as message param for `error` method.\n\n### BrowserMonitoringService\n\n#### constructor\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```\ninterface RemoteMonitoringServiceParams {\n  serviceName?: string;\n  serviceVersion?: string;\n  serviceEnv?: string;\n  authToken?: string;\n}\n```\n\n- `RemoteMonitoringServiceConfig` - datadog params passed to init function. More info in [docs](https://docs.datadoghq.com/logs/log_collection/javascript/#initialization-parameters)\n- `serviceName` - name of the service\n- `serviceVersion` - version of the service\n- `serviceEnv` - environment where service is deployed\n- `authToken` - client token\n\n### ServerMonitoringService\n\n#### constructor\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```\ninterface RemoteMonitoringServiceConfig {\n  transportOptions?: Partial<TransportBaseOptions>;\n  loggerOptions?: Partial<LoggerOptions>;\n}\n```\n\n- `transportOptions` - [pino-datadog-transport options](https://github.com/theogravity/pino-datadog-transport#configuration-options)\n- `loggerOptions` - [pino logger options](https://getpino.io/#/docs/api?id=options-object)\n\n#### overrideLogger\n\nOverrides logger passed as argument with monitoring logger.\nAll methods of this logger will be overridden with corresponding methods of server monitoring.\n\n```\noverrideLogger(unknownLogger: UnknownLogger)\n```\n\n```\ninterface UnknownLogger {\n  log?(...parts: unknown[]): void;\n  debug?(...parts: unknown[]): void;\n  info(...parts: unknown[]): void;\n  warn(...parts: unknown[]): void;\n  error(...parts: unknown[]): void;\n}\n```\n\n#### overrideNativeConsole\n\nCalls overrideLogger for native console.\n\n```\noverrideNativeConsole()\n```\n\n#### catchProcessErrors\n\nSubscribes to `unhandledRejection` and `uncaughtException` events of the process to report `error` in such cases.\n\n```\ncatchProcessErrors()\n```\n\n### ServerMonitoringService\n\n#### initServerMonitoring\n\nInstantiate ServerMonitoringService with provided params and may also do additional work,\ndepending on provided variables.\n\n```\ninitServerMonitoring = (\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  monitoringOptions?: MonitoringOptions,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig\n): ServerMonitoringService\n```\n\n```\ninterface MonitoringOptions {\n  shouldOverrideNativeConsole?: boolean;\n  shouldCatchProcessErrors?: boolean;\n  globalMonitoringInstanceName?: string;\n}\n```\n\n- `RemoteMonitoringServiceParams` and `RemoteMonitoringServiceConfig` are same as in `constructor` api\n- `shouldOverrideNativeConsole` - if `true`, will call `serverMonitoringService.overrideNativeConsole()` under the hood\n- `shouldCatchProcessErrors` - if `true`, will call `serverMonitoringService.catchProcessErrors()` under the hood\n- `globalMonitoringInstanceName` - if provided with non-empty string will put instantiated `serverMonitoringService` into global scope under provided name.\n","readmeFilename":"README.md","gitHead":"ffc59fec5c3d00a9b3ae570e84787686f2d39912","description":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)","_id":"@kilohealth/web-app-monitoring@1.2.1-alpha.3","_nodeVersion":"18.16.1","_npmVersion":"9.7.2","dist":{"integrity":"sha512-iWy+Lv7WiOwkUpSISXG/wFizdvx7r3/pTD/cippNHKJMrNbaEiZaOxeSdIr9gOeSrK3qtojB6sALKiqSLuozsQ==","shasum":"065864b7df393be3ef74bfabcfb5a42fff914bac","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.2.1-alpha.3.tgz","fileCount":40,"unpackedSize":54146,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCNCqRZ+Rkg1wgotQ1OKUE76lQKbfWOOclsQ44mYe7PkAIhAJSW7jQhuY9ET1FlPpmo9woAHtMsaKPcbTh1d5KbL534"}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.2.1-alpha.3_1688149751165_0.557498329552091"},"_hasShrinkwrap":false},"1.2.1":{"name":"@kilohealth/web-app-monitoring","version":"1.2.1","license":"MIT","author":{"name":"Kilo Health"},"exports":{"./dist/server/initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./dist/server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./dist/browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","githooks:commit-msg":"commitlint --edit","githooks:pre-commit":"lint-staged","prepare":"husky install","semantic-release":"semantic-release","test":"jest"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","dd-trace":"^4.0.0","deepmerge":"^4.3.1","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","@types/lodash":"^4.14.195","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jest-environment-jsdom":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"gitHead":"6ad7067ce14fa967d695a10582713045e9b98949","description":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)","_id":"@kilohealth/web-app-monitoring@1.2.1","_nodeVersion":"18.16.1","_npmVersion":"9.7.2","dist":{"integrity":"sha512-7aNviv74oCj2Mql55714/Ev7irVj3SoUWjiEw9BuW/8+x5fkk9JRrs1W6qIUDnSpXi7RccuKJPz3QZqjTr/3bA==","shasum":"9b9dd0ae119d219dffcc6893e1c19beade1937a9","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.2.1.tgz","fileCount":40,"unpackedSize":54138,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDvtdd8xLN9Lx/ZGpjpH4ddwF3WBhlr/itu1AMjP5wmpQIgD0U8Smmd/zNLNqyjIE1LFndGjQCpdzhbXCYSW+Lb9o0="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.2.1_1688462759564_0.864854031688769"},"_hasShrinkwrap":false},"1.2.1-alpha.4":{"name":"@kilohealth/web-app-monitoring","version":"1.2.1-alpha.4","license":"MIT","author":{"name":"Kilo Health"},"sideEffects":false,"exports":{"./dist/server/initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./dist/server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./dist/browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","githooks:commit-msg":"commitlint --edit","githooks:pre-commit":"lint-staged","prepare":"husky install","semantic-release":"semantic-release","test":"jest"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","dd-trace":"^4.0.0","deepmerge":"^4.3.1","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","@types/lodash":"^4.14.195","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jest-environment-jsdom":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"readme":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)\n\n# @kilohealth/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\n## The Idea\n\nPackage was created to abstract away underlying level of monitoring,\nto make it easy to setup monitoring for any env as well as to make it easy to migrate from DataDog to other monitoring solution.\n\nThe package consists of 3 parts:\n\n- browser monitoring\n- CLI (needed to upload sourcemaps for browser monitoring)\n- server monitoring\n\n## Browser monitoring setup:\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n> If you are migrating from direct datadog integration - don’t forget to remove @datadog/browser-logs and @datadog/datadog-ci. Those are now deps of @kilohealth/web-app-monitoring.\n\n```\nnpm uninstall @datadog/browser-logs @datadog/datadog-ci\n```\n\n### Add build phase env variables\n\n- `MONITORING_TOOL__API_KEY` - this key is needed in order to uploaded sourcemaps for browser monitoring. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048).\n- `MONITORING_TOOL__SERVICE_NAME`, `MONITORING_TOOL__SERVICE_VERSION` and `MONITORING_TOOL__SERVICE_ENV`- the service name, version and env. These variables will be used by client code as well as by cli. Most client frameworks will not expose all node build phase env vars, so you probably need to reexpose them with prefix to switch on automatic replacement for client code during client build. In particular\n  - For NextJS - you have to add prefix `NEXT_PUBLIC_` to each of them. For example you have to add not only `MONITORING_TOOL__SERVICE_NAME=timely-hand-web-funnel-app` but also `NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME`. See more in [docs](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#exposing-environment-variables-to-the-browser).\n  - For GatsbyJS - you have to add prefix `GATSBY_`. See more in [docs](https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/#accessing-environment-variables-in-the-browser)\n  - For ViteJS - you have to add prefix `VITE_`. See more in [docs](https://vitejs.dev/guide/env-and-mode.html#env-files).\n- `MONITORING_TOOL__CLIENT_TOKEN` - this is client side token, which need to be built into client code in order to send logs into DD server.\n  Because it is needed on client you will have to re-expose it using same approach as variables above (probably prefixing env var).\n  Token you can create or find [here](https://app.datadoghq.com/organization-settings/client-tokens).\n  > PS: theoretically you can avoid creating `MONITORING_TOOL__CLIENT_TOKEN` env variable and create only `NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN` instead, because this variable is not needed for CLI to work. But for the sake of SRP we advocate for sticking with same reexposing approach here.\n\n#### Example of full env variables setup for client monitoring:\n\n##### Expose client token to be able to reexpose it for client-side code:\n\n```\nMONITORING_TOOL__CLIENT_TOKEN=pub2_your_client_token\n```\n\n##### Set variables for source map upload CLI to work during the build phase:\n\n```\nMONITORING_TOOL__SERVICE_ENV=$CI_ENVIRONMENT_NAME\n```\n\n```\nMONITORING_TOOL__SERVICE_NAME=greantess-funnel\n```\n\n```\nMONITORING_TOOL__SERVICE_VERSION=$CI_COMMIT_SHA\n```\n\n```\nMONITORING_TOOL__API_KEY=4be_your_api_key\n```\n\n##### Reexpose for your framework to client-side code(NextJS example):\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV=$MONITORING_TOOL__SERVICE_ENV\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION=$MONITORING_TOOL__SERVICE_VERSION\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN=$MONITORING_TOOL__CLIENT_TOKEN\n```\n\n### Modify your build code to generate sourcemaps, depending on env variable\n\nIdeally we don't want to generate and upload sourcemaps during each build.\nIn order to opt-in for this behavior sometimes we need to make additional configuration changes in our build process.\nWe need to build sourcemaps only in case specific env variable `IS_SOURCEMAP_UPLOAD_BUILD` is provided.\nWe don't provide it for dev or debug builds, only for production.\nThese are articles on how to do this for different frameworks and examples.\n\n- [Vite](https://vitejs.dev/config/build-options.html#build-sourcemap)\n\n```\nexport default defineConfig({\n  ...\n  build: {\n    sourcemap: Boolean(process.env.IS_SOURCEMAP_UPLOAD_BUILD),\n    ...\n  }\n  ...\n})\n```\n\n- [NextJS](https://nextjs.org/docs/pages/api-reference/next-config-js/productionBrowserSourceMaps)\n\n```\nmodule.exports = {\n  ...\n  productionBrowserSourceMaps: Boolean(process.env.IS_SOURCEMAP_UPLOAD_BUILD),\n  ...\n}\n```\n\n- Gatsby is a little bit more tricky. It generates sourcemaps by default. In order to prevent this you can add code to\n\n```\nmodule.exports.onCreateWebpackConfig = ({ stage, actions }) => {\n  // build-javascript is prod build phase\n  if (stage === 'build-javascript') {\n    actions.setWebpackConfig({\n      // hidden-source-map removes last line from final files,\n      // to avoid contenthash mismatch between builds\n      // we don't want sourcemaps in prod by default\n      devtool: process.env.IS_SOURCEMAP_UPLOAD_BUILD\n        ? 'hidden-source-map'\n        : false,\n    });\n  }\n};\n```\n\nThere is also an [article](https://akashrajpurohit.com/blog/disable-source-maps-in-gatsbyjs-v2/) with more details.\n\n### Add build and upload sourcemaps script to scripts section\n\n#### Prepare sourcemap upload build\n\nIt should run bin from our lib called `web-app-monitoring__upload-sourcemaps`.\nFor this script to work you would need to provide it with two variables\n\n- `MONITORING_TOOL__PUBLIC_PATH` - this is RELATIVE path, part of URL between domain (which can be different for different environments) and path to file itself.\n  In other words - base path for all the assets within your application.\n  You can think of this as kind of relative [Public Path | webpack](https://webpack.js.org/guides/public-path/).\n  For example it can be `/` or `/static`.\n  In other words this is common relative prefix for all your static files or / if there is none.\n  - for Vite default is `/`\n  - for NextJS default is `/_next/static/chunks` (!!! `_` instead of `.` in file system)\n  - for GatsbyJS default is `/`\n- `MONITORING_TOOL__BUILD_DIR` - this should be RELATIVE path to your build directory. For example `./dist` or `./build`.\n  - for Vite default is `./dist`\n  - for NextJS default is `./.next/static/chunks`\n  - for GatsbyJS default is `./public`\n\nExample for NextJS\n\n```\nMONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\n```\n\n#### 1. Approach with parallel build\n\nWe advocate for this approach.\nWith it you have separate script to build code for deployment and another one to make a build with sourcemaps to upload those to monitoring tool.\nWe decided to extract source map building and uploading into separate step because:\n\n- not each build may need these, and it will increase build time. For example, you may want to avoid this for dev builds.\n- we don’t want to manually alter the build (aka removing sourcemaps from it) because it is fragile and hard to maintain\n- we don’t want sourcemaps to leak into production, so we wanna separate generating them and uploading files into prod into different processes\n\nExample for NextJS\n\n```\n\"upload:sourcemaps\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 npm run build && MONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\"\n```\n\nAnd then your CI should run `upload:sourcemaps` script in parallel with main build,\nto avoid blocking and increasing main build time.\n\n#### 2. Approach with same build\n\nThere is alternative approach to tweak main build process with sourcemaps.\nWe need to do next things:\n\n- switch sourcemaps to be hidden.\n  For example instead of using option sourcemaps use hidden-sourcemaps.\n  With this we will avoid warning in console in production regarding the fact that files have a reference to sourcemaps but sourcemaps are not found.\n  We are just removing this reference during build phase.\n\n- extend usual build script with flag to include sourcemaps `IS_SOURCEMAP_UPLOAD_BUILD`,\n  script to upload them and script to remove them. For example\n\n```\n\"only-upload:sourcemaps\": \"MONITORING_TOOL__BUILD_DIR=./public MONITORING_TOOL__PUBLIC_PATH=/ web-app-monitoring__upload-sourcemaps\",\n\"remove:sourcemaps\": \"find ./public -name \\\"*.map\\\" -type f -delete\",\n\"build\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 gatsby build --prefix-paths && npm run only-upload:sourcemaps && npm run remove:sourcemaps\",\n```\n\n### Usage: Import and instantiate BrowserMonitoringService\n\n> Important note: there is no single entry point for package. You can't do smth like `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring';`\n> Reason for that is to avoid bundling server-code into client bundle and vice versa. This structure will ensure effective tree shaking during build time.\n\n> In case your bundler supports package.json `exports` field - you can also omit `dist` in path folder `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/browser';`\n\n```\nimport { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/dist/browser';\n\nexport const monitoring = new BrowserMonitoringService({\n  authToken: NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN,\n  serviceName: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV,\n})\n```\n\nAs you can see we are using here all our exposed variables. If any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n#### OPTIONAL: If you are using React you may benefit from utilizing [Error Boundaries](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary):\n\n```\nimport React, { Component, PropsWithChildren } from 'react';\nimport { monitoring } from '../services/monitoring';\n\ninterface ErrorBoundaryProps {}\ninterface ErrorBoundaryState {\n  hasError: boolean;\n}\n\nexport class ErrorBoundary extends Component<\n  PropsWithChildren<ErrorBoundaryProps>,\n  ErrorBoundaryState\n> {\n  static getDerivedStateFromError(_: Error): ErrorBoundaryState {\n    return { hasError: true };\n  }\n\n  state: ErrorBoundaryState = {\n    hasError: false,\n  };\n\n  componentDidCatch(error: Error) {\n    monitoring.reportError(error);\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return <h1>Sorry... There was an error</h1>;\n    }\n\n    return this.props.children;\n  }\n}\n```\n\n## Server monitoring setup (NextJS):\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n### Add build phase env variables\n\n- `MONITORING_TOOL__API_KEY` - this key is needed in order to send logs. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048).\n- `MONITORING_TOOL__SERVICE_NAME`, `MONITORING_TOOL__SERVICE_VERSION` and `MONITORING_TOOL__SERVICE_ENV` - the service name, version and env. These variables will be used to send logs.\n\n#### Example of full env variables setup for server monitoring:\n\n##### Set variables for sending logs:\n\n```\nMONITORING_TOOL__SERVICE_ENV=$CI_ENVIRONMENT_NAME\n```\n\n```\nMONITORING_TOOL__SERVICE_NAME=greantess-funnel\n```\n\n```\nMONITORING_TOOL__SERVICE_VERSION=$CI_COMMIT_SHA\n```\n\n```\nMONITORING_TOOL__API_KEY=4be_your_api_key\n```\n\n### Usage\n\n#### Approach with facade\n\n`initServerMonitoring` is a facade over `ServerMonitoringService`.\nYou can instantiate and use that service directly.\nThis function basically does one thing - instantiate it and can do 3 more additional things:\n\n- call `overrideNativeConsole` - method of the service to override native console, to log to datadog instead.\n- cal `catchProcessErrors` - method of the service to subscribe to native errors, to log them to datadog.\n- put service itself into global scope under defined name, so serverside code can use it.\n\nYou may wonder why we instantiate service here and not in server-side code.\nThe reason for that is if we override the native console and catch native errors - we would like to set up this as soon as possible.\nIf you don’t care too much about the very first seconds of next server - you can use alternative simpler server side logging solution.\n\nExample(for NextJS):\n\n- update next.config.ts to include into start script of production server code\n  `next.config.ts:`\n\n```\nconst { initServerMonitoring } = require('@kilohealth/web-app-monitoring/dist/server');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    const remoteMonitoringServiceParams = {\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    };\n    const config = {\n      shouldOverrideNativeConsole: true,\n      shouldCatchProcessErrors: true,\n      globalMonitoringInstanceName: 'kiloServerMonitoring',\n    }\n    initServerMonitoring(remoteMonitoringServiceParams, config);\n  }\n}\n```\n\n- update `custom.d.ts` file to declare that global scope now have monitoring service as a prop\n  In order to use ServerMonitoringService instance in other parts of code via global we need to let TS know\n  that we added new property to global object.\n  In NextJS you can just create or add next code into `custom.d.ts` file in root of the project.\n  Be aware that var name matches string that you provided in code above (kiloServerMonitoring in this case).\n  `custom.d.ts:`\n\n```\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\ndeclare global {\n  // eslint-disable-next-line no-var\n  var kiloServerMonitoring: ServerMonitoringService;\n}\n```\n\n- use it in code\n\n```\nexport const getHomeServerSideProps = async context => {\n  global.kiloServerMonitoring.info('getHomeServerSideProps called');\n  ...\n};\n```\n\n#### Approach with direct instantiation\n\nIf you don’t care too much about catching native errors or native logs\nin the early stages of your server app -\nyou can avoid sharing logger via global scope and instead initialize it inside of app.\n\n```\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\nexport const monitoring = new ServerMonitoringService({\n  authToken: MONITORING_TOOL__API_KEY,\n  serviceName: MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: MONITORING_TOOL__SERVICE_ENV,\n})\n```\n\nAs you can see we are using here all our env variables.\nIf any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n### Tracing setup:\n\nWe need to connect tracing as soon as possible during code, so it can be injected into all base modules for APM monitoring.\nTracing module is available via:\n\n```\nconst { initTracing } = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\ninitTracing({\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n});\n```\n\nExample(NextJS):\n\n```\nconst { PHASE_PRODUCTION_SERVER } = require('next/constants');\nconst { initTracing } = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    initTracing({\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    });\n  }\n}\n```\n\n> In newer versions of NextJS there is experimental feature called\n> [instrumentationHook](https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation)\n> We can opt out from using undocumented `PHASE_PRODUCTION_SERVER` to use `instrumentationHook` for tracing init.\n\n> There is also possibility to use `NODE_OPTIONS='-r ./prestart-script.js ' next start` instead.\n> But there is an issue with `pino-datadog-transport`, which for performance reason spawns separate thread for log sending to data-dog and it this option seems to be passed to that process as well which triggers an infinite loop of require and initialization.\n\n## API\n\n### MonitoringService (both BrowserMonitoringService and ServerMonitoringService have these methods)\n\n#### debug, info, warn\n\n```\ndebug(message: string, context?: object)\ninfo(message: string, context?: object)\nwarn(message: string, context?: object)\n```\n\n- `message` - any message to be logged\n- `context` - object with all needed and related to the log entrance data\n\n#### error\n\n```\nerror(message: string, context?: object, error?: Error)\n```\n\nSame as above, but you can also optionally pass error instance as thrid parameter\n\n#### reportError\n\n```\nreportError(error: Error, context?: object)\n```\n\nShortcut for `service.error()`, which uses `error.message` field as message param for `error` method.\n\n### BrowserMonitoringService\n\n#### constructor\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```\ninterface RemoteMonitoringServiceParams {\n  serviceName?: string;\n  serviceVersion?: string;\n  serviceEnv?: string;\n  authToken?: string;\n}\n```\n\n- `RemoteMonitoringServiceConfig` - datadog params passed to init function. More info in [docs](https://docs.datadoghq.com/logs/log_collection/javascript/#initialization-parameters)\n- `serviceName` - name of the service\n- `serviceVersion` - version of the service\n- `serviceEnv` - environment where service is deployed\n- `authToken` - client token\n\n### ServerMonitoringService\n\n#### constructor\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```\ninterface RemoteMonitoringServiceConfig {\n  transportOptions?: Partial<TransportBaseOptions>;\n  loggerOptions?: Partial<LoggerOptions>;\n}\n```\n\n- `transportOptions` - [pino-datadog-transport options](https://github.com/theogravity/pino-datadog-transport#configuration-options)\n- `loggerOptions` - [pino logger options](https://getpino.io/#/docs/api?id=options-object)\n\n#### overrideLogger\n\nOverrides logger passed as argument with monitoring logger.\nAll methods of this logger will be overridden with corresponding methods of server monitoring.\n\n```\noverrideLogger(unknownLogger: UnknownLogger)\n```\n\n```\ninterface UnknownLogger {\n  log?(...parts: unknown[]): void;\n  debug?(...parts: unknown[]): void;\n  info(...parts: unknown[]): void;\n  warn(...parts: unknown[]): void;\n  error(...parts: unknown[]): void;\n}\n```\n\n#### overrideNativeConsole\n\nCalls overrideLogger for native console.\n\n```\noverrideNativeConsole()\n```\n\n#### catchProcessErrors\n\nSubscribes to `unhandledRejection` and `uncaughtException` events of the process to report `error` in such cases.\n\n```\ncatchProcessErrors()\n```\n\n### ServerMonitoringService\n\n#### initServerMonitoring\n\nInstantiate ServerMonitoringService with provided params and may also do additional work,\ndepending on provided variables.\n\n```\ninitServerMonitoring = (\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  monitoringOptions?: MonitoringOptions,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig\n): ServerMonitoringService\n```\n\n```\ninterface MonitoringOptions {\n  shouldOverrideNativeConsole?: boolean;\n  shouldCatchProcessErrors?: boolean;\n  globalMonitoringInstanceName?: string;\n}\n```\n\n- `RemoteMonitoringServiceParams` and `RemoteMonitoringServiceConfig` are same as in `constructor` api\n- `shouldOverrideNativeConsole` - if `true`, will call `serverMonitoringService.overrideNativeConsole()` under the hood\n- `shouldCatchProcessErrors` - if `true`, will call `serverMonitoringService.catchProcessErrors()` under the hood\n- `globalMonitoringInstanceName` - if provided with non-empty string will put instantiated `serverMonitoringService` into global scope under provided name.\n","readmeFilename":"README.md","gitHead":"837db5305a504be96cbc3cece43d212828db953c","description":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)","_id":"@kilohealth/web-app-monitoring@1.2.1-alpha.4","_nodeVersion":"18.16.1","_npmVersion":"9.7.2","dist":{"integrity":"sha512-/8jaR64PCIlzurZJkUafpvgrLLpx88iMDaQ63OOF6yUkuaDmDrQR1FwrESDZKY0UWa+0D4BHE0eb0d8FmDaCIA==","shasum":"48ed39e85892122ed43f9167d8bf95dcc5652189","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.2.1-alpha.4.tgz","fileCount":40,"unpackedSize":54170,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHsNapNrMut3ktFCszT1lSY0+D5gIYQzp8s56FtbK5wPAiAfEDTCDcZ2A8y9hsJN+iZXAHXXB1flGQBdCkHC0yvemA=="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.2.1-alpha.4_1688527685117_0.37145411142001183"},"_hasShrinkwrap":false},"1.2.2-alpha.1":{"name":"@kilohealth/web-app-monitoring","version":"1.2.2-alpha.1","license":"MIT","author":{"name":"Kilo Health"},"sideEffects":false,"exports":{"./dist/server/initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./dist/server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./dist/browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","githooks:commit-msg":"commitlint --edit","githooks:pre-commit":"lint-staged","prepare":"husky install","semantic-release":"semantic-release","test":"jest"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","dd-trace":"^4.0.0","deepmerge":"^4.3.1","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","@types/lodash":"^4.14.195","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jest-environment-jsdom":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"readme":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)\n\n# @kilohealth/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\n## The Idea\n\nPackage was created to abstract away underlying level of monitoring,\nto make it easy to setup monitoring for any env as well as to make it easy to migrate from DataDog to other monitoring solution.\n\nThe package consists of 3 parts:\n\n- browser monitoring\n- CLI (needed to upload sourcemaps for browser monitoring)\n- server monitoring\n\n## Browser monitoring setup:\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n> If you are migrating from direct datadog integration - don’t forget to remove @datadog/browser-logs and @datadog/datadog-ci. Those are now deps of @kilohealth/web-app-monitoring.\n\n```\nnpm uninstall @datadog/browser-logs @datadog/datadog-ci\n```\n\n### Add build phase env variables\n\n- `MONITORING_TOOL__API_KEY` - this key is needed in order to uploaded sourcemaps for browser monitoring. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048).\n- `MONITORING_TOOL__SERVICE_NAME`, `MONITORING_TOOL__SERVICE_VERSION` and `MONITORING_TOOL__SERVICE_ENV`- the service name, version and env. These variables will be used by client code as well as by cli. Most client frameworks will not expose all node build phase env vars, so you probably need to reexpose them with prefix to switch on automatic replacement for client code during client build. In particular\n  - For NextJS - you have to add prefix `NEXT_PUBLIC_` to each of them. For example you have to add not only `MONITORING_TOOL__SERVICE_NAME=timely-hand-web-funnel-app` but also `NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME`. See more in [docs](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#exposing-environment-variables-to-the-browser).\n  - For GatsbyJS - you have to add prefix `GATSBY_`. See more in [docs](https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/#accessing-environment-variables-in-the-browser)\n  - For ViteJS - you have to add prefix `VITE_`. See more in [docs](https://vitejs.dev/guide/env-and-mode.html#env-files).\n- `MONITORING_TOOL__CLIENT_TOKEN` - this is client side token, which need to be built into client code in order to send logs into DD server.\n  Because it is needed on client you will have to re-expose it using same approach as variables above (probably prefixing env var).\n  Token you can create or find [here](https://app.datadoghq.com/organization-settings/client-tokens).\n  > PS: theoretically you can avoid creating `MONITORING_TOOL__CLIENT_TOKEN` env variable and create only `NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN` instead, because this variable is not needed for CLI to work. But for the sake of SRP we advocate for sticking with same reexposing approach here.\n\n#### Example of full env variables setup for client monitoring:\n\n##### Expose client token to be able to reexpose it for client-side code:\n\n```\nMONITORING_TOOL__CLIENT_TOKEN=pub2_your_client_token\n```\n\n##### Set variables for source map upload CLI to work during the build phase:\n\n```\nMONITORING_TOOL__SERVICE_ENV=$CI_ENVIRONMENT_NAME\n```\n\n```\nMONITORING_TOOL__SERVICE_NAME=greantess-funnel\n```\n\n```\nMONITORING_TOOL__SERVICE_VERSION=$CI_COMMIT_SHA\n```\n\n```\nMONITORING_TOOL__API_KEY=4be_your_api_key\n```\n\n##### Reexpose for your framework to client-side code(NextJS example):\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV=$MONITORING_TOOL__SERVICE_ENV\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION=$MONITORING_TOOL__SERVICE_VERSION\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN=$MONITORING_TOOL__CLIENT_TOKEN\n```\n\n### Modify your build code to generate sourcemaps, depending on env variable\n\nIdeally we don't want to generate and upload sourcemaps during each build.\nIn order to opt-in for this behavior sometimes we need to make additional configuration changes in our build process.\nWe need to build sourcemaps only in case specific env variable `IS_SOURCEMAP_UPLOAD_BUILD` is provided.\nWe don't provide it for dev or debug builds, only for production.\nThese are articles on how to do this for different frameworks and examples.\n\n- [Vite](https://vitejs.dev/config/build-options.html#build-sourcemap)\n\n```\nexport default defineConfig({\n  ...\n  build: {\n    sourcemap: Boolean(process.env.IS_SOURCEMAP_UPLOAD_BUILD),\n    ...\n  }\n  ...\n})\n```\n\n- [NextJS](https://nextjs.org/docs/pages/api-reference/next-config-js/productionBrowserSourceMaps)\n\n```\nmodule.exports = {\n  ...\n  productionBrowserSourceMaps: Boolean(process.env.IS_SOURCEMAP_UPLOAD_BUILD),\n  ...\n}\n```\n\n- Gatsby is a little bit more tricky. It generates sourcemaps by default. In order to prevent this you can add code to\n\n```\nmodule.exports.onCreateWebpackConfig = ({ stage, actions }) => {\n  // build-javascript is prod build phase\n  if (stage === 'build-javascript') {\n    actions.setWebpackConfig({\n      // hidden-source-map removes last line from final files,\n      // to avoid contenthash mismatch between builds\n      // we don't want sourcemaps in prod by default\n      devtool: process.env.IS_SOURCEMAP_UPLOAD_BUILD\n        ? 'hidden-source-map'\n        : false,\n    });\n  }\n};\n```\n\nThere is also an [article](https://akashrajpurohit.com/blog/disable-source-maps-in-gatsbyjs-v2/) with more details.\n\n### Add build and upload sourcemaps script to scripts section\n\n#### Prepare sourcemap upload build\n\nIt should run bin from our lib called `web-app-monitoring__upload-sourcemaps`.\nFor this script to work you would need to provide it with two variables\n\n- `MONITORING_TOOL__PUBLIC_PATH` - this is RELATIVE path, part of URL between domain (which can be different for different environments) and path to file itself.\n  In other words - base path for all the assets within your application.\n  You can think of this as kind of relative [Public Path | webpack](https://webpack.js.org/guides/public-path/).\n  For example it can be `/` or `/static`.\n  In other words this is common relative prefix for all your static files or / if there is none.\n  - for Vite default is `/`\n  - for NextJS default is `/_next/static/chunks` (!!! `_` instead of `.` in file system)\n  - for GatsbyJS default is `/`\n- `MONITORING_TOOL__BUILD_DIR` - this should be RELATIVE path to your build directory. For example `./dist` or `./build`.\n  - for Vite default is `./dist`\n  - for NextJS default is `./.next/static/chunks`\n  - for GatsbyJS default is `./public`\n\nExample for NextJS\n\n```\nMONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\n```\n\n#### 1. Approach with parallel build\n\nWe advocate for this approach.\nWith it you have separate script to build code for deployment and another one to make a build with sourcemaps to upload those to monitoring tool.\nWe decided to extract source map building and uploading into separate step because:\n\n- not each build may need these, and it will increase build time. For example, you may want to avoid this for dev builds.\n- we don’t want to manually alter the build (aka removing sourcemaps from it) because it is fragile and hard to maintain\n- we don’t want sourcemaps to leak into production, so we wanna separate generating them and uploading files into prod into different processes\n\nExample for NextJS\n\n```\n\"upload:sourcemaps\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 npm run build && MONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\"\n```\n\nAnd then your CI should run `upload:sourcemaps` script in parallel with main build,\nto avoid blocking and increasing main build time.\n\n#### 2. Approach with same build\n\nThere is alternative approach to tweak main build process with sourcemaps.\nWe need to do next things:\n\n- switch sourcemaps to be hidden.\n  For example instead of using option sourcemaps use hidden-sourcemaps.\n  With this we will avoid warning in console in production regarding the fact that files have a reference to sourcemaps but sourcemaps are not found.\n  We are just removing this reference during build phase.\n\n- extend usual build script with flag to include sourcemaps `IS_SOURCEMAP_UPLOAD_BUILD`,\n  script to upload them and script to remove them. For example\n\n```\n\"only-upload:sourcemaps\": \"MONITORING_TOOL__BUILD_DIR=./public MONITORING_TOOL__PUBLIC_PATH=/ web-app-monitoring__upload-sourcemaps\",\n\"remove:sourcemaps\": \"find ./public -name \\\"*.map\\\" -type f -delete\",\n\"build\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 gatsby build --prefix-paths && npm run only-upload:sourcemaps && npm run remove:sourcemaps\",\n```\n\n### Usage: Import and instantiate BrowserMonitoringService\n\n> Important note: there is no single entry point for package. You can't do smth like `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring';`\n> Reason for that is to avoid bundling server-code into client bundle and vice versa. This structure will ensure effective tree shaking during build time.\n\n> In case your bundler supports package.json `exports` field - you can also omit `dist` in path folder `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/browser';`\n\n```\nimport { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/dist/browser';\n\nexport const monitoring = new BrowserMonitoringService({\n  authToken: NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN,\n  serviceName: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV,\n})\n```\n\nAs you can see we are using here all our exposed variables. If any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n#### OPTIONAL: If you are using React you may benefit from utilizing [Error Boundaries](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary):\n\n```\nimport React, { Component, PropsWithChildren } from 'react';\nimport { monitoring } from '../services/monitoring';\n\ninterface ErrorBoundaryProps {}\ninterface ErrorBoundaryState {\n  hasError: boolean;\n}\n\nexport class ErrorBoundary extends Component<\n  PropsWithChildren<ErrorBoundaryProps>,\n  ErrorBoundaryState\n> {\n  static getDerivedStateFromError(_: Error): ErrorBoundaryState {\n    return { hasError: true };\n  }\n\n  state: ErrorBoundaryState = {\n    hasError: false,\n  };\n\n  componentDidCatch(error: Error) {\n    monitoring.reportError(error);\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return <h1>Sorry... There was an error</h1>;\n    }\n\n    return this.props.children;\n  }\n}\n```\n\n## Server monitoring setup (NextJS):\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n### Add build phase env variables\n\n- `MONITORING_TOOL__API_KEY` - this key is needed in order to send logs. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048).\n- `MONITORING_TOOL__SERVICE_NAME`, `MONITORING_TOOL__SERVICE_VERSION` and `MONITORING_TOOL__SERVICE_ENV` - the service name, version and env. These variables will be used to send logs.\n\n#### Example of full env variables setup for server monitoring:\n\n##### Set variables for sending logs:\n\n```\nMONITORING_TOOL__SERVICE_ENV=$CI_ENVIRONMENT_NAME\n```\n\n```\nMONITORING_TOOL__SERVICE_NAME=greantess-funnel\n```\n\n```\nMONITORING_TOOL__SERVICE_VERSION=$CI_COMMIT_SHA\n```\n\n```\nMONITORING_TOOL__API_KEY=4be_your_api_key\n```\n\n### Usage\n\n#### Approach with facade\n\n`initServerMonitoring` is a facade over `ServerMonitoringService`.\nYou can instantiate and use that service directly.\nThis function basically does one thing - instantiate it and can do 3 more additional things:\n\n- call `overrideNativeConsole` - method of the service to override native console, to log to datadog instead.\n- cal `catchProcessErrors` - method of the service to subscribe to native errors, to log them to datadog.\n- put service itself into global scope under defined name, so serverside code can use it.\n\nYou may wonder why we instantiate service here and not in server-side code.\nThe reason for that is if we override the native console and catch native errors - we would like to set up this as soon as possible.\nIf you don’t care too much about the very first seconds of next server - you can use alternative simpler server side logging solution.\n\nExample(for NextJS):\n\n- update next.config.ts to include into start script of production server code\n  `next.config.ts:`\n\n```\nconst { initServerMonitoring } = require('@kilohealth/web-app-monitoring/dist/server');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    const remoteMonitoringServiceParams = {\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    };\n    const config = {\n      shouldOverrideNativeConsole: true,\n      shouldCatchProcessErrors: true,\n      globalMonitoringInstanceName: 'kiloServerMonitoring',\n    }\n    initServerMonitoring(remoteMonitoringServiceParams, config);\n  }\n}\n```\n\n- update `custom.d.ts` file to declare that global scope now have monitoring service as a prop\n  In order to use ServerMonitoringService instance in other parts of code via global we need to let TS know\n  that we added new property to global object.\n  In NextJS you can just create or add next code into `custom.d.ts` file in root of the project.\n  Be aware that var name matches string that you provided in code above (kiloServerMonitoring in this case).\n  `custom.d.ts:`\n\n```\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\ndeclare global {\n  // eslint-disable-next-line no-var\n  var kiloServerMonitoring: ServerMonitoringService;\n}\n```\n\n- use it in code\n\n```\nexport const getHomeServerSideProps = async context => {\n  global.kiloServerMonitoring.info('getHomeServerSideProps called');\n  ...\n};\n```\n\n#### Approach with direct instantiation\n\nIf you don’t care too much about catching native errors or native logs\nin the early stages of your server app -\nyou can avoid sharing logger via global scope and instead initialize it inside of app.\n\n```\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\nexport const monitoring = new ServerMonitoringService({\n  authToken: MONITORING_TOOL__API_KEY,\n  serviceName: MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: MONITORING_TOOL__SERVICE_ENV,\n})\n```\n\nAs you can see we are using here all our env variables.\nIf any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n### Tracing setup:\n\nWe need to connect tracing as soon as possible during code, so it can be injected into all base modules for APM monitoring.\nTracing module is available via:\n\n```\nconst { initTracing } = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\ninitTracing({\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n});\n```\n\nExample(NextJS):\n\n```\nconst { PHASE_PRODUCTION_SERVER } = require('next/constants');\nconst { initTracing } = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    initTracing({\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    });\n  }\n}\n```\n\n> In newer versions of NextJS there is experimental feature called\n> [instrumentationHook](https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation)\n> We can opt out from using undocumented `PHASE_PRODUCTION_SERVER` to use `instrumentationHook` for tracing init.\n\n> There is also possibility to use `NODE_OPTIONS='-r ./prestart-script.js ' next start` instead.\n> But there is an issue with `pino-datadog-transport`, which for performance reason spawns separate thread for log sending to data-dog and it this option seems to be passed to that process as well which triggers an infinite loop of require and initialization.\n\n## API\n\n### MonitoringService (both BrowserMonitoringService and ServerMonitoringService have these methods)\n\n#### debug, info, warn\n\n```\ndebug(message: string, context?: object)\ninfo(message: string, context?: object)\nwarn(message: string, context?: object)\n```\n\n- `message` - any message to be logged\n- `context` - object with all needed and related to the log entrance data\n\n#### error\n\n```\nerror(message: string, context?: object, error?: Error)\n```\n\nSame as above, but you can also optionally pass error instance as thrid parameter\n\n#### reportError\n\n```\nreportError(error: Error, context?: object)\n```\n\nShortcut for `service.error()`, which uses `error.message` field as message param for `error` method.\n\n### BrowserMonitoringService\n\n#### constructor\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```\ninterface RemoteMonitoringServiceParams {\n  serviceName?: string;\n  serviceVersion?: string;\n  serviceEnv?: string;\n  authToken?: string;\n}\n```\n\n- `RemoteMonitoringServiceConfig` - datadog params passed to init function. More info in [docs](https://docs.datadoghq.com/logs/log_collection/javascript/#initialization-parameters)\n- `serviceName` - name of the service\n- `serviceVersion` - version of the service\n- `serviceEnv` - environment where service is deployed\n- `authToken` - client token\n\n### ServerMonitoringService\n\n#### constructor\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```\ninterface RemoteMonitoringServiceConfig {\n  transportOptions?: Partial<TransportBaseOptions>;\n  loggerOptions?: Partial<LoggerOptions>;\n}\n```\n\n- `transportOptions` - [pino-datadog-transport options](https://github.com/theogravity/pino-datadog-transport#configuration-options)\n- `loggerOptions` - [pino logger options](https://getpino.io/#/docs/api?id=options-object)\n\n#### overrideLogger\n\nOverrides logger passed as argument with monitoring logger.\nAll methods of this logger will be overridden with corresponding methods of server monitoring.\n\n```\noverrideLogger(unknownLogger: UnknownLogger)\n```\n\n```\ninterface UnknownLogger {\n  log?(...parts: unknown[]): void;\n  debug?(...parts: unknown[]): void;\n  info(...parts: unknown[]): void;\n  warn(...parts: unknown[]): void;\n  error(...parts: unknown[]): void;\n}\n```\n\n#### overrideNativeConsole\n\nCalls overrideLogger for native console.\n\n```\noverrideNativeConsole()\n```\n\n#### catchProcessErrors\n\nSubscribes to `unhandledRejection` and `uncaughtException` events of the process to report `error` in such cases.\n\n```\ncatchProcessErrors()\n```\n\n### ServerMonitoringService\n\n#### initServerMonitoring\n\nInstantiate ServerMonitoringService with provided params and may also do additional work,\ndepending on provided variables.\n\n```\ninitServerMonitoring = (\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  monitoringOptions?: MonitoringOptions,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig\n): ServerMonitoringService\n```\n\n```\ninterface MonitoringOptions {\n  shouldOverrideNativeConsole?: boolean;\n  shouldCatchProcessErrors?: boolean;\n  globalMonitoringInstanceName?: string;\n}\n```\n\n- `RemoteMonitoringServiceParams` and `RemoteMonitoringServiceConfig` are same as in `constructor` api\n- `shouldOverrideNativeConsole` - if `true`, will call `serverMonitoringService.overrideNativeConsole()` under the hood\n- `shouldCatchProcessErrors` - if `true`, will call `serverMonitoringService.catchProcessErrors()` under the hood\n- `globalMonitoringInstanceName` - if provided with non-empty string will put instantiated `serverMonitoringService` into global scope under provided name.\n","readmeFilename":"README.md","gitHead":"ed32d184d4d81b5a25f50b6b1cedce917c9e453d","description":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)","_id":"@kilohealth/web-app-monitoring@1.2.2-alpha.1","_nodeVersion":"18.16.1","_npmVersion":"9.7.2","dist":{"integrity":"sha512-5Cp+WJk/7Z/Rd6IMCG+RId5h9rVSNaOPrOh8z1aBHOHPzZ1CgzwEgO0yJjx2qyuSJ29s/gcaTGZCTAEkcfxMcw==","shasum":"91fa67f04d772a0042c75a00cf7ba85fcf8b583b","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.2.2-alpha.1.tgz","fileCount":40,"unpackedSize":54170,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBjZ6EXc7bPnOiPSoPfqkx6WpKFeLxasRa13P31iacpoAiA58xFUMTNINoB8G2SR5EU2biE4QQJYeQKrtIZ46cVbdQ=="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.2.2-alpha.1_1688529132553_0.49586196960857265"},"_hasShrinkwrap":false},"1.2.2-alpha.2":{"name":"@kilohealth/web-app-monitoring","version":"1.2.2-alpha.2","license":"MIT","author":{"name":"Kilo Health"},"sideEffects":false,"exports":{"./dist/server/initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./dist/server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./dist/browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","githooks:commit-msg":"commitlint --edit","githooks:pre-commit":"lint-staged","prepare":"husky install","semantic-release":"semantic-release","test":"jest"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","dd-trace":"^4.0.0","deepmerge":"^4.3.1","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","@types/lodash":"^4.14.195","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jest-environment-jsdom":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"readme":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)\n\n# @kilohealth/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\n## The Idea\n\nPackage was created to abstract away underlying level of monitoring,\nto make it easy to setup monitoring for any env as well as to make it easy to migrate from DataDog to other monitoring solution.\n\nThe package consists of 3 parts:\n\n- browser monitoring\n- CLI (needed to upload sourcemaps for browser monitoring)\n- server monitoring\n\n## Browser monitoring setup:\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n> If you are migrating from direct datadog integration - don’t forget to remove @datadog/browser-logs and @datadog/datadog-ci. Those are now deps of @kilohealth/web-app-monitoring.\n\n```\nnpm uninstall @datadog/browser-logs @datadog/datadog-ci\n```\n\n### Add build phase env variables\n\n- `MONITORING_TOOL__API_KEY` - this key is needed in order to uploaded sourcemaps for browser monitoring. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048).\n- `MONITORING_TOOL__SERVICE_NAME`, `MONITORING_TOOL__SERVICE_VERSION` and `MONITORING_TOOL__SERVICE_ENV`- the service name, version and env. These variables will be used by client code as well as by cli. Most client frameworks will not expose all node build phase env vars, so you probably need to reexpose them with prefix to switch on automatic replacement for client code during client build. In particular\n  - For NextJS - you have to add prefix `NEXT_PUBLIC_` to each of them. For example you have to add not only `MONITORING_TOOL__SERVICE_NAME=timely-hand-web-funnel-app` but also `NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME`. See more in [docs](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#exposing-environment-variables-to-the-browser).\n  - For GatsbyJS - you have to add prefix `GATSBY_`. See more in [docs](https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/#accessing-environment-variables-in-the-browser)\n  - For ViteJS - you have to add prefix `VITE_`. See more in [docs](https://vitejs.dev/guide/env-and-mode.html#env-files).\n- `MONITORING_TOOL__CLIENT_TOKEN` - this is client side token, which need to be built into client code in order to send logs into DD server.\n  Because it is needed on client you will have to re-expose it using same approach as variables above (probably prefixing env var).\n  Token you can create or find [here](https://app.datadoghq.com/organization-settings/client-tokens).\n  > PS: theoretically you can avoid creating `MONITORING_TOOL__CLIENT_TOKEN` env variable and create only `NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN` instead, because this variable is not needed for CLI to work. But for the sake of SRP we advocate for sticking with same reexposing approach here.\n\n#### Example of full env variables setup for client monitoring:\n\n##### Expose client token to be able to reexpose it for client-side code:\n\n```\nMONITORING_TOOL__CLIENT_TOKEN=pub2_your_client_token\n```\n\n##### Set variables for source map upload CLI to work during the build phase:\n\n```\nMONITORING_TOOL__SERVICE_ENV=$CI_ENVIRONMENT_NAME\n```\n\n```\nMONITORING_TOOL__SERVICE_NAME=greantess-funnel\n```\n\n```\nMONITORING_TOOL__SERVICE_VERSION=$CI_COMMIT_SHA\n```\n\n```\nMONITORING_TOOL__API_KEY=4be_your_api_key\n```\n\n##### Reexpose for your framework to client-side code(NextJS example):\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV=$MONITORING_TOOL__SERVICE_ENV\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION=$MONITORING_TOOL__SERVICE_VERSION\n```\n\n```\nNEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN=$MONITORING_TOOL__CLIENT_TOKEN\n```\n\n### Modify your build code to generate sourcemaps, depending on env variable\n\nIdeally we don't want to generate and upload sourcemaps during each build.\nIn order to opt-in for this behavior sometimes we need to make additional configuration changes in our build process.\nWe need to build sourcemaps only in case specific env variable `IS_SOURCEMAP_UPLOAD_BUILD` is provided.\nWe don't provide it for dev or debug builds, only for production.\nThese are articles on how to do this for different frameworks and examples.\n\n- [Vite](https://vitejs.dev/config/build-options.html#build-sourcemap)\n\n```\nexport default defineConfig({\n  ...\n  build: {\n    sourcemap: Boolean(process.env.IS_SOURCEMAP_UPLOAD_BUILD),\n    ...\n  }\n  ...\n})\n```\n\n- [NextJS](https://nextjs.org/docs/pages/api-reference/next-config-js/productionBrowserSourceMaps)\n\n```\nmodule.exports = {\n  ...\n  productionBrowserSourceMaps: Boolean(process.env.IS_SOURCEMAP_UPLOAD_BUILD),\n  ...\n}\n```\n\n- Gatsby is a little bit more tricky. It generates sourcemaps by default. In order to prevent this you can add code to\n\n```\nmodule.exports.onCreateWebpackConfig = ({ stage, actions }) => {\n  // build-javascript is prod build phase\n  if (stage === 'build-javascript') {\n    actions.setWebpackConfig({\n      // hidden-source-map removes last line from final files,\n      // to avoid contenthash mismatch between builds\n      // we don't want sourcemaps in prod by default\n      devtool: process.env.IS_SOURCEMAP_UPLOAD_BUILD\n        ? 'hidden-source-map'\n        : false,\n    });\n  }\n};\n```\n\nThere is also an [article](https://akashrajpurohit.com/blog/disable-source-maps-in-gatsbyjs-v2/) with more details.\n\n### Add build and upload sourcemaps script to scripts section\n\n#### Prepare sourcemap upload build\n\nIt should run bin from our lib called `web-app-monitoring__upload-sourcemaps`.\nFor this script to work you would need to provide it with two variables\n\n- `MONITORING_TOOL__PUBLIC_PATH` - this is RELATIVE path, part of URL between domain (which can be different for different environments) and path to file itself.\n  In other words - base path for all the assets within your application.\n  You can think of this as kind of relative [Public Path | webpack](https://webpack.js.org/guides/public-path/).\n  For example it can be `/` or `/static`.\n  In other words this is common relative prefix for all your static files or / if there is none.\n  - for Vite default is `/`\n  - for NextJS default is `/_next/static/chunks` (!!! `_` instead of `.` in file system)\n  - for GatsbyJS default is `/`\n- `MONITORING_TOOL__BUILD_DIR` - this should be RELATIVE path to your build directory. For example `./dist` or `./build`.\n  - for Vite default is `./dist`\n  - for NextJS default is `./.next/static/chunks`\n  - for GatsbyJS default is `./public`\n\nExample for NextJS\n\n```\nMONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\n```\n\n#### 1. Approach with parallel build\n\nWe advocate for this approach.\nWith it you have separate script to build code for deployment and another one to make a build with sourcemaps to upload those to monitoring tool.\nWe decided to extract source map building and uploading into separate step because:\n\n- not each build may need these, and it will increase build time. For example, you may want to avoid this for dev builds.\n- we don’t want to manually alter the build (aka removing sourcemaps from it) because it is fragile and hard to maintain\n- we don’t want sourcemaps to leak into production, so we wanna separate generating them and uploading files into prod into different processes\n\nExample for NextJS\n\n```\n\"upload:sourcemaps\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 npm run build && MONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\"\n```\n\nAnd then your CI should run `upload:sourcemaps` script in parallel with main build,\nto avoid blocking and increasing main build time.\n\n#### 2. Approach with same build\n\nThere is alternative approach to tweak main build process with sourcemaps.\nWe need to do next things:\n\n- switch sourcemaps to be hidden.\n  For example instead of using option sourcemaps use hidden-sourcemaps.\n  With this we will avoid warning in console in production regarding the fact that files have a reference to sourcemaps but sourcemaps are not found.\n  We are just removing this reference during build phase.\n\n- extend usual build script with flag to include sourcemaps `IS_SOURCEMAP_UPLOAD_BUILD`,\n  script to upload them and script to remove them. For example\n\n```\n\"only-upload:sourcemaps\": \"MONITORING_TOOL__BUILD_DIR=./public MONITORING_TOOL__PUBLIC_PATH=/ web-app-monitoring__upload-sourcemaps\",\n\"remove:sourcemaps\": \"find ./public -name \\\"*.map\\\" -type f -delete\",\n\"build\": \"IS_SOURCEMAP_UPLOAD_BUILD=1 gatsby build --prefix-paths && npm run only-upload:sourcemaps && npm run remove:sourcemaps\",\n```\n\n### Usage: Import and instantiate BrowserMonitoringService\n\n> Important note: there is no single entry point for package. You can't do smth like `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring';`\n> Reason for that is to avoid bundling server-code into client bundle and vice versa. This structure will ensure effective tree shaking during build time.\n\n> In case your bundler supports package.json `exports` field - you can also omit `dist` in path folder `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/browser';`\n\n```\nimport { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/dist/browser';\n\nexport const monitoring = new BrowserMonitoringService({\n  authToken: NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN,\n  serviceName: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV,\n})\n```\n\nAs you can see we are using here all our exposed variables. If any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n#### OPTIONAL: If you are using React you may benefit from utilizing [Error Boundaries](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary):\n\n```\nimport React, { Component, PropsWithChildren } from 'react';\nimport { monitoring } from '../services/monitoring';\n\ninterface ErrorBoundaryProps {}\ninterface ErrorBoundaryState {\n  hasError: boolean;\n}\n\nexport class ErrorBoundary extends Component<\n  PropsWithChildren<ErrorBoundaryProps>,\n  ErrorBoundaryState\n> {\n  static getDerivedStateFromError(_: Error): ErrorBoundaryState {\n    return { hasError: true };\n  }\n\n  state: ErrorBoundaryState = {\n    hasError: false,\n  };\n\n  componentDidCatch(error: Error) {\n    monitoring.reportError(error);\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return <h1>Sorry... There was an error</h1>;\n    }\n\n    return this.props.children;\n  }\n}\n```\n\n## Server monitoring setup (NextJS):\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n### Add build phase env variables\n\n- `MONITORING_TOOL__API_KEY` - this key is needed in order to send logs. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048).\n- `MONITORING_TOOL__SERVICE_NAME`, `MONITORING_TOOL__SERVICE_VERSION` and `MONITORING_TOOL__SERVICE_ENV` - the service name, version and env. These variables will be used to send logs.\n\n#### Example of full env variables setup for server monitoring:\n\n##### Set variables for sending logs:\n\n```\nMONITORING_TOOL__SERVICE_ENV=$CI_ENVIRONMENT_NAME\n```\n\n```\nMONITORING_TOOL__SERVICE_NAME=greantess-funnel\n```\n\n```\nMONITORING_TOOL__SERVICE_VERSION=$CI_COMMIT_SHA\n```\n\n```\nMONITORING_TOOL__API_KEY=4be_your_api_key\n```\n\n### Usage\n\n#### Approach with facade\n\n`initServerMonitoring` is a facade over `ServerMonitoringService`.\nYou can instantiate and use that service directly.\nThis function basically does one thing - instantiate it and can do 3 more additional things:\n\n- call `overrideNativeConsole` - method of the service to override native console, to log to datadog instead.\n- cal `catchProcessErrors` - method of the service to subscribe to native errors, to log them to datadog.\n- put service itself into global scope under defined name, so serverside code can use it.\n\nYou may wonder why we instantiate service here and not in server-side code.\nThe reason for that is if we override the native console and catch native errors - we would like to set up this as soon as possible.\nIf you don’t care too much about the very first seconds of next server - you can use alternative simpler server side logging solution.\n\nExample(for NextJS):\n\n- update next.config.ts to include into start script of production server code\n  `next.config.ts:`\n\n```\nconst { initServerMonitoring } = require('@kilohealth/web-app-monitoring/dist/server');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    const remoteMonitoringServiceParams = {\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    };\n    const config = {\n      shouldOverrideNativeConsole: true,\n      shouldCatchProcessErrors: true,\n      globalMonitoringInstanceName: 'kiloServerMonitoring',\n    }\n    initServerMonitoring(remoteMonitoringServiceParams, config);\n  }\n}\n```\n\n- update `custom.d.ts` file to declare that global scope now have monitoring service as a prop\n  In order to use ServerMonitoringService instance in other parts of code via global we need to let TS know\n  that we added new property to global object.\n  In NextJS you can just create or add next code into `custom.d.ts` file in root of the project.\n  Be aware that var name matches string that you provided in code above (kiloServerMonitoring in this case).\n  `custom.d.ts:`\n\n```\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\ndeclare global {\n  // eslint-disable-next-line no-var\n  var kiloServerMonitoring: ServerMonitoringService;\n}\n```\n\n- use it in code\n\n```\nexport const getHomeServerSideProps = async context => {\n  global.kiloServerMonitoring.info('getHomeServerSideProps called');\n  ...\n};\n```\n\n#### Approach with direct instantiation\n\nIf you don’t care too much about catching native errors or native logs\nin the early stages of your server app -\nyou can avoid sharing logger via global scope and instead initialize it inside of app.\n\n```\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\nexport const monitoring = new ServerMonitoringService({\n  authToken: MONITORING_TOOL__API_KEY,\n  serviceName: MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: MONITORING_TOOL__SERVICE_ENV,\n})\n```\n\nAs you can see we are using here all our env variables.\nIf any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n### Tracing setup:\n\nWe need to connect tracing as soon as possible during code, so it can be injected into all base modules for APM monitoring.\nTracing module is available via:\n\n```\nconst { initTracing } = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\ninitTracing({\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n});\n```\n\nExample(NextJS):\n\n```\nconst { PHASE_PRODUCTION_SERVER } = require('next/constants');\nconst { initTracing } = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    initTracing({\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    });\n  }\n}\n```\n\n> In newer versions of NextJS there is experimental feature called\n> [instrumentationHook](https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation)\n> We can opt out from using undocumented `PHASE_PRODUCTION_SERVER` to use `instrumentationHook` for tracing init.\n\n> There is also possibility to use `NODE_OPTIONS='-r ./prestart-script.js ' next start` instead.\n> But there is an issue with `pino-datadog-transport`, which for performance reason spawns separate thread for log sending to data-dog and it this option seems to be passed to that process as well which triggers an infinite loop of require and initialization.\n\n## API\n\n### MonitoringService (both BrowserMonitoringService and ServerMonitoringService have these methods)\n\n#### debug, info, warn\n\n```\ndebug(message: string, context?: object)\ninfo(message: string, context?: object)\nwarn(message: string, context?: object)\n```\n\n- `message` - any message to be logged\n- `context` - object with all needed and related to the log entrance data\n\n#### error\n\n```\nerror(message: string, context?: object, error?: Error)\n```\n\nSame as above, but you can also optionally pass error instance as thrid parameter\n\n#### reportError\n\n```\nreportError(error: Error, context?: object)\n```\n\nShortcut for `service.error()`, which uses `error.message` field as message param for `error` method.\n\n### BrowserMonitoringService\n\n#### constructor\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```\ninterface RemoteMonitoringServiceParams {\n  serviceName?: string;\n  serviceVersion?: string;\n  serviceEnv?: string;\n  authToken?: string;\n}\n```\n\n- `RemoteMonitoringServiceConfig` - datadog params passed to init function. More info in [docs](https://docs.datadoghq.com/logs/log_collection/javascript/#initialization-parameters)\n- `serviceName` - name of the service\n- `serviceVersion` - version of the service\n- `serviceEnv` - environment where service is deployed\n- `authToken` - client token\n\n### ServerMonitoringService\n\n#### constructor\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```\ninterface RemoteMonitoringServiceConfig {\n  transportOptions?: Partial<TransportBaseOptions>;\n  loggerOptions?: Partial<LoggerOptions>;\n}\n```\n\n- `transportOptions` - [pino-datadog-transport options](https://github.com/theogravity/pino-datadog-transport#configuration-options)\n- `loggerOptions` - [pino logger options](https://getpino.io/#/docs/api?id=options-object)\n\n#### overrideLogger\n\nOverrides logger passed as argument with monitoring logger.\nAll methods of this logger will be overridden with corresponding methods of server monitoring.\n\n```\noverrideLogger(unknownLogger: UnknownLogger)\n```\n\n```\ninterface UnknownLogger {\n  log?(...parts: unknown[]): void;\n  debug?(...parts: unknown[]): void;\n  info(...parts: unknown[]): void;\n  warn(...parts: unknown[]): void;\n  error(...parts: unknown[]): void;\n}\n```\n\n#### overrideNativeConsole\n\nCalls overrideLogger for native console.\n\n```\noverrideNativeConsole()\n```\n\n#### catchProcessErrors\n\nSubscribes to `unhandledRejection` and `uncaughtException` events of the process to report `error` in such cases.\n\n```\ncatchProcessErrors()\n```\n\n### ServerMonitoringService\n\n#### initServerMonitoring\n\nInstantiate ServerMonitoringService with provided params and may also do additional work,\ndepending on provided variables.\n\n```\ninitServerMonitoring = (\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  monitoringOptions?: MonitoringOptions,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig\n): ServerMonitoringService\n```\n\n```\ninterface MonitoringOptions {\n  shouldOverrideNativeConsole?: boolean;\n  shouldCatchProcessErrors?: boolean;\n  globalMonitoringInstanceName?: string;\n}\n```\n\n- `RemoteMonitoringServiceParams` and `RemoteMonitoringServiceConfig` are same as in `constructor` api\n- `shouldOverrideNativeConsole` - if `true`, will call `serverMonitoringService.overrideNativeConsole()` under the hood\n- `shouldCatchProcessErrors` - if `true`, will call `serverMonitoringService.catchProcessErrors()` under the hood\n- `globalMonitoringInstanceName` - if provided with non-empty string will put instantiated `serverMonitoringService` into global scope under provided name.\n","readmeFilename":"README.md","gitHead":"2b84a8b4d13e7604529df0a27f5582e1056b243d","description":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)","_id":"@kilohealth/web-app-monitoring@1.2.2-alpha.2","_nodeVersion":"18.16.1","_npmVersion":"9.7.2","dist":{"integrity":"sha512-AgbY7OiyeIM5oXDCLqCEFC5HMXUdnXzNAbzkhKfcxRZlyjdsOkb6GUTaNrxB9d/oKHzE73HG0F7PlpfK1fS/UQ==","shasum":"3ae36da349302eb9b6dea288f257c96e36e59b6b","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.2.2-alpha.2.tgz","fileCount":40,"unpackedSize":54178,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDQxbb4W1DMjtT1Td5G0DadNj5ddCDy4dgJv3nPeQEnvQIhANuX7bkdbDSsaUPWq+dS6BITd7pC/T1d3VagQY2oNVw/"}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.2.2-alpha.2_1688620409196_0.9470753735527289"},"_hasShrinkwrap":false},"1.2.2":{"name":"@kilohealth/web-app-monitoring","version":"1.2.2","license":"MIT","author":{"name":"Kilo Health"},"sideEffects":false,"exports":{"./dist/server/initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./dist/server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./dist/browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","githooks:commit-msg":"commitlint --edit","githooks:pre-commit":"lint-staged","prepare":"husky install","semantic-release":"semantic-release","test":"jest"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","dd-trace":"^4.0.0","deepmerge":"^4.3.1","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","@types/lodash":"^4.14.195","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jest-environment-jsdom":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"gitHead":"8a1842c334affe85ec11cc60f06865ca6552d110","description":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)","_id":"@kilohealth/web-app-monitoring@1.2.2","_nodeVersion":"18.16.1","_npmVersion":"9.7.2","dist":{"integrity":"sha512-NUN15P213WsIPcycCoP/8pz8sHnjiwWRRwW6Eb6zzKSOfsSglJ+srztjXm0F56ZGoIvGmeJCQysvVQ/2ejHc+w==","shasum":"65e4517fba91c71feb0658d58c77e503732942f9","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.2.2.tgz","fileCount":40,"unpackedSize":54170,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHldMNwDGFIINvA/g43I5BkxNeOzufhRFwJZBeizQVK9AiEAhK5tfOj3FjuYAfy7dRj/U3+B16hTtzbRj4J/J1E3WbM="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.2.2_1688620711093_0.5729702730804018"},"_hasShrinkwrap":false},"1.3.0-alpha.1":{"name":"@kilohealth/web-app-monitoring","version":"1.3.0-alpha.1","license":"MIT","author":{"name":"Kilo Health"},"sideEffects":false,"exports":{"./dist/server/initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./dist/server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./dist/browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","githooks:commit-msg":"commitlint --edit","githooks:pre-commit":"lint-staged","prepare":"husky install","semantic-release":"semantic-release","test":"jest"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","dd-trace":"^4.0.0","deepmerge":"^4.3.1","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","@types/lodash":"^4.14.195","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jest-environment-jsdom":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"readme":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)\n\n# @kilohealth/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\n## The Idea\n\nPackage was created to abstract away underlying level of monitoring,\nto make it easy to setup monitoring for any env as well as to make it easy to migrate from DataDog to other monitoring solution.\n\nThe package consists of 3 parts:\n\n- Browser / Client monitoring (browser logs)\n- CLI (needed to upload sourcemaps for browser monitoring)\n- Server monitoring (server logs, APM, tracing)\n\n## Getting Started\n\n> **Note:** If you are migrating from direct datadog integration - don’t forget to remove `@datadog/...` dependencies. Those are now dependencies of `@kilohealth/web-app-monitoring`.\n>\n> ```\n> npm uninstall @datadog/...\n> ```\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n### Setup environment variables\n\n| Variable                           | Description                                                                                                                                                                                                                                              | Upload source maps | Server (APM, tracing) | Browser / Client |\n| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------: | :-------------------: | :--------------: |\n| `MONITORING_TOOL__API_KEY`         | This key is needed in order to uploaded source maps for browser monitoring, send server side (APM) logs and tracing info. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048). |         ✔️         |          ✔️           |                  |\n| `MONITORING_TOOL__SERVICE_NAME`    | The service name, for example: `timely-hand-web-funnel-app`.                                                                                                                                                                                             |         ✔️         |          ✔️           |        ✔️        |\n| `MONITORING_TOOL__SERVICE_VERSION` | The service version, for example: `$CI_COMMIT_SHA`.                                                                                                                                                                                                      |         ✔️         |          ✔️           |        ✔️        |\n| `MONITORING_TOOL__SERVICE_ENV`     | The service environment, for example: `$CI_ENVIRONMENT_NAME`.                                                                                                                                                                                            |         ✔️         |          ✔️           |        ✔️        |\n| `MONITORING_TOOL__CLIENT_TOKEN`    | This token is needed in order to send browser monitoring logs. You can create or find client token [here](https://app.datadoghq.com/organization-settings/client-tokens).                                                                                |         ️          |                       |        ✔️        |\n\n> **Note:** Depending on the framework you are using, in order to expose environment variables to the client you may need to prefix the environment variables as mentioned below:\n>\n> - For Next.js, add the prefix `NEXT_PUBLIC_` to each variable. Refer to the [documentation](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#bundling-environment-variables-for-the-browser) for more details.\n> - For Gatsby.js, add the prefix `GATSBY_` to each variable. Refer to the [documentation](https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/#accessing-environment-variables-in-the-browser) for more details.\n> - For Vite.js, add the prefix `VITE_` to each variable. Refer to the [documentation](https://vitejs.dev/guide/env-and-mode.html) for more details.\n\n> **Tip:** By following Single Source of Truth principle you can reexport variables, needed for the client, in the build stage (Next.js example):\n>\n> ```\n> NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME\n> NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION=$MONITORING_TOOL__SERVICE_VERSION\n> NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV=$MONITORING_TOOL__SERVICE_ENV\n> ```\n\n### Setup browser monitoring\n\n#### Generate hidden source maps\n\nIn order to upload source maps into the monitoring service we need to include those source map files into our build.\nThis can be done by slightly altering the build phase bundler configuration of our app:\n\n<details>\n<summary>Next.js (next.config.js)</summary>\n\n```js\nmodule.exports = {\n  webpack: (config, context) => {\n    const isClient = !context.isServer;\n    const isProd = !context.dev;\n    const isSourcemapsUploadEnabled = Boolean(\n      process.env.MONITORING_TOOL__API_KEY,\n    );\n\n    // Generate source maps only for the client side production build\n    if (isClient && isProd && isSourcemapsUploadEnabled) {\n      return {\n        ...config,\n        // No reference. No source maps exposure to the client (browser).\n        // Hidden source maps generation only for error reporting purposes.\n        devtool: 'hidden-source-map',\n      };\n    }\n\n    return config;\n  },\n};\n```\n\nRefer to the [documentation](https://webpack.js.org/configuration/devtool/) for more details.\n\n</details>\n\n<details>\n<summary>Gatsby.js (gatsby-node.js)</summary>\n\n```js\nmodule.exports = {\n  onCreateWebpackConfig: ({ stage, actions }) => {\n    const isSourcemapsUploadEnabled = Boolean(\n      process.env.MONITORING_TOOL__API_KEY,\n    );\n    // build-javascript is prod build phase\n    if (stage === 'build-javascript' && isSourcemapsUploadEnabled) {\n      actions.setWebpackConfig({\n        // No reference. No source maps exposure to the client (browser).\n        // Hidden source maps generation only for error reporting purposes.\n        devtool: 'hidden-source-map',\n      });\n    }\n  },\n};\n```\n\nRefer to the [documentation](https://webpack.js.org/configuration/devtool/) for more details.\n\n</details>\n\n<details>\n<summary>Vite.js (vite.config.js)</summary>\n\n```js\nexport default defineConfig({\n  build: {\n    // No reference. No source maps exposure to the client (browser).\n    // Hidden source maps generation only for error reporting purposes.\n    sourcemap: process.env.MONITORING_TOOL__API_KEY ? 'hidden' : false,\n  },\n});\n```\n\nRefer to the [documentation](https://vitejs.dev/config/build-options.html#build-sourcemap) for more details.\n\n</details>\n\n> **Note:** We are using `hidden source maps` only for error reporting purposes.\n> That means our source maps are not exposed to the client\n> and there are no references to those source maps in our source code.\n\n#### Upload generated source maps\n\nIn order to upload generated source maps into the monitoring service, you should use `web-app-monitoring__upload-sourcemaps` bin, provided by `@kilohealth/web-app-monitoring` package.\nTo run the script you need to provide arguments:\n\n| Argument               | Description                                                                                                                                                                                                                                                                                                                                                                                                                                             |   Vite   |                              Next                               |   Gatsby   |\n| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------: | :-------------------------------------------------------------: | :--------: |\n| `--buildDir` or `-d`   | This should be RELATIVE path to your build directory. For example `./dist` or `./build`.                                                                                                                                                                                                                                                                                                                                                                | `./dist` |                     `./.next/static/chunks`                     | `./public` |\n| `--publicPath` or `-p` | This is RELATIVE path, part of URL between domain (which can be different for different environments) and path to file itself. In other words - base path for all the assets within your application.<br/>You can think of this as kind of relative [Public Path](https://webpack.js.org/guides/public-path/). For example it can be `/` or `/static`. In other words this is common relative prefix for all your static files or `/` if there is none. |   `/`    | `/_next/static/chunks` (!!! `_` instead of `.` in file system)️ |    `/`     |\n\nScript example for Next.js:\n\n```\n\"scripts\": {\n  \"upload:sourcemaps\": \"web-app-monitoring__upload-sourcemaps --buildDir=./.next/static/chunks --publicPath=/_next/static/chunks\",\n  ...\n},\n```\n\nAnd then your CI should run `upload:sourcemaps` script for the build that includes generated source maps.\n\n### Browser Monitoring Usage\n\n> **Important note:** There is no single entry point for package. You can't do something like `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring';`\n> Reason for that is to avoid bundling server-code into client bundle and vice versa. This structure will ensure effective tree shaking during build time.\n\n> In case your bundler supports package.json `exports` field - you can also omit `dist` in path folder `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/browser';`\n\n```ts\nimport { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/dist/browser';\n\nexport const monitoring = new BrowserMonitoringService({\n  authToken: NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN,\n  serviceName: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV,\n});\n```\n\nAs you can see we are using here all our exposed variables. If any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n**OPTIONAL: If you are using React you may benefit from utilizing [Error Boundaries](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary):**\n\n```tsx\nimport React, { Component, PropsWithChildren } from 'react';\nimport { monitoring } from '../services/monitoring';\n\ninterface ErrorBoundaryProps {}\ninterface ErrorBoundaryState {\n  hasError: boolean;\n}\n\nexport class ErrorBoundary extends Component<\n  PropsWithChildren<ErrorBoundaryProps>,\n  ErrorBoundaryState\n> {\n  static getDerivedStateFromError(_: Error): ErrorBoundaryState {\n    return { hasError: true };\n  }\n\n  state: ErrorBoundaryState = {\n    hasError: false,\n  };\n\n  componentDidCatch(error: Error) {\n    monitoring.reportError(error);\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return <h1>Sorry... There was an error</h1>;\n    }\n\n    return this.props.children;\n  }\n}\n```\n\n### Setup Server Monitoring (Next.js)\n\n<details>\n<summary>Approach with facade</summary>\n\n`initServerMonitoring` is a facade over `ServerMonitoringService`.\nYou can instantiate and use that service directly.\nThis function basically does one thing - instantiate it and can do 3 more additional things:\n\n- call `overrideNativeConsole` - method of the service to override native console, to log to datadog instead.\n- cal `catchProcessErrors` - method of the service to subscribe to native errors, to log them to datadog.\n- put service itself into global scope under defined name, so serverside code can use it.\n\nYou may wonder why we instantiate service here and not in server-side code.\nThe reason for that is if we override the native console and catch native errors - we would like to set up this as soon as possible.\nIf you don’t care too much about the very first seconds of next server - you can use alternative simpler server side logging solution.\n\nExample for Next.js:\n\n- update next.config.ts to include into start script of production server code\n  `next.config.ts:`\n\n```js\nconst {\n  initServerMonitoring,\n} = require('@kilohealth/web-app-monitoring/dist/server');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    const remoteMonitoringServiceParams = {\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    };\n    const config = {\n      shouldOverrideNativeConsole: true,\n      shouldCatchProcessErrors: true,\n      globalMonitoringInstanceName: 'kiloServerMonitoring',\n    };\n    initServerMonitoring(remoteMonitoringServiceParams, config);\n  }\n};\n```\n\n- update `custom.d.ts` file to declare that global scope now have monitoring service as a prop\n  In order to use ServerMonitoringService instance in other parts of code via global we need to let TS know\n  that we added new property to global object.\n  In Next.js you can just create or add next code into `custom.d.ts` file in root of the project.\n  Be aware that var name matches string that you provided in code above (kiloServerMonitoring in this case).\n  `custom.d.ts:`\n\n```ts\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\ndeclare global {\n  // eslint-disable-next-line no-var\n  var kiloServerMonitoring: ServerMonitoringService;\n}\n```\n\n- use it in code\n\n```ts\nexport const getHomeServerSideProps = async context => {\n  global.kiloServerMonitoring.info('getHomeServerSideProps called');\n};\n```\n\n</details>\n\n<details>\n<summary>Approach with direct instantiation</summary>\n\nIf you don’t care too much about catching native errors or native logs\nin the early stages of your server app -\nyou can avoid sharing logger via global scope and instead initialize it inside of app.\n\n```ts\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\nexport const monitoring = new ServerMonitoringService({\n  authToken: MONITORING_TOOL__API_KEY,\n  serviceName: MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: MONITORING_TOOL__SERVICE_ENV,\n});\n```\n\nAs you can see we are using here all our env variables.\nIf any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n</details>\n\n#### Init Tracing\n\nWe need to connect tracing as soon as possible during code, so it can be injected into all base modules for APM monitoring.\nTracing module is available via:\n\n```js\nconst {\n  initTracing,\n} = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\n\ninitTracing({\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n});\n```\n\nExample for Next.js:\n\n```js\nconst { PHASE_PRODUCTION_SERVER } = require('next/constants');\nconst {\n  initTracing,\n} = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    initTracing({\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    });\n  }\n};\n```\n\n> **Note:** In newer versions of Next.js there is experimental feature called\n> [instrumentationHook](https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation)\n> We can opt out from using undocumented `PHASE_PRODUCTION_SERVER` to use `instrumentationHook` for tracing init.\n> There is also possibility to use `NODE_OPTIONS='-r ./prestart-script.js ' next start` instead.\n> But there is an issue with `pino-datadog-transport`, which for performance reason spawns separate thread for log sending to data-dog and it this option seems to be passed to that process as well which triggers an infinite loop of require and initialization.\n\n## API\n\n### MonitoringService (both BrowserMonitoringService and ServerMonitoringService have these methods)\n\n<details>\n<summary>debug, info, warn</summary>\n\n```\ndebug(message: string, context?: object)\ninfo(message: string, context?: object)\nwarn(message: string, context?: object)\n```\n\n- `message` - any message to be logged\n- `context` - object with all needed and related to the log entrance data\n</details>\n\n<details>\n<summary>error</summary>\n\n```\nerror(message: string, context?: object, error?: Error)\n```\n\nSame as above, but you can also optionally pass error instance as third parameter\n\n</details>\n\n<details>\n<summary>reportError</summary>\n\n```\nreportError(error: Error, context?: object)\n```\n\nShortcut for `service.error()`, which uses `error.message` field as message param for `error` method.\n\n</details>\n\n### BrowserMonitoringService\n\n<details>\n<summary>constructor</summary>\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```ts\ninterface RemoteMonitoringServiceParams {\n  serviceName?: string;\n  serviceVersion?: string;\n  serviceEnv?: string;\n  authToken?: string;\n}\n```\n\n- `RemoteMonitoringServiceConfig` - datadog params passed to init function. More info in [docs](https://docs.datadoghq.com/logs/log_collection/javascript/#initialization-parameters)\n- `serviceName` - name of the service\n- `serviceVersion` - version of the service\n- `serviceEnv` - environment where service is deployed\n- `authToken` - client token\n</details>\n\n### ServerMonitoringService\n\n<details>\n<summary>constructor</summary>\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```ts\ninterface RemoteMonitoringServiceConfig {\n  transportOptions?: Partial<TransportBaseOptions>;\n  loggerOptions?: Partial<LoggerOptions>;\n}\n```\n\n- `transportOptions` - [pino-datadog-transport options](https://github.com/theogravity/pino-datadog-transport#configuration-options)\n- `loggerOptions` - [pino logger options](https://getpino.io/#/docs/api?id=options-object)\n</details>\n\n<details>\n<summary>overrideLogger</summary>\n\nOverrides logger passed as argument with monitoring logger.\nAll methods of this logger will be overridden with corresponding methods of server monitoring.\n\n```\noverrideLogger(unknownLogger: UnknownLogger)\n```\n\n```ts\ninterface UnknownLogger {\n  log?(...parts: unknown[]): void;\n  debug?(...parts: unknown[]): void;\n  info(...parts: unknown[]): void;\n  warn(...parts: unknown[]): void;\n  error(...parts: unknown[]): void;\n}\n```\n\n</details>\n\n<details>\n<summary>overrideNativeConsole</summary>\n\nCalls overrideLogger for native console.\n\n```\noverrideNativeConsole()\n```\n\n</details>\n\n<details>\n<summary>catchProcessErrors</summary>\n\nSubscribes to `unhandledRejection` and `uncaughtException` events of the process to report `error` in such cases.\n\n```ts\ncatchProcessErrors();\n```\n\n</details>\n\n### ServerMonitoringService\n\n<details>\n<summary>initServerMonitoring</summary>\n\nInstantiate ServerMonitoringService with provided params and may also do additional work,\ndepending on provided variables.\n\n```\ninitServerMonitoring = (\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  monitoringOptions?: MonitoringOptions,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig\n): ServerMonitoringService\n```\n\n```ts\ninterface MonitoringOptions {\n  shouldOverrideNativeConsole?: boolean;\n  shouldCatchProcessErrors?: boolean;\n  globalMonitoringInstanceName?: string;\n}\n```\n\n- `RemoteMonitoringServiceParams` and `RemoteMonitoringServiceConfig` are same as in `constructor` api\n- `shouldOverrideNativeConsole` - if `true`, will call `serverMonitoringService.overrideNativeConsole()` under the hood\n- `shouldCatchProcessErrors` - if `true`, will call `serverMonitoringService.catchProcessErrors()` under the hood\n- `globalMonitoringInstanceName` - if provided with non-empty string will put instantiated `serverMonitoringService` into global scope under provided name.\n\n</details>\n","readmeFilename":"README.md","gitHead":"9feaf8369444ea277f943adcfc28066c481281e7","description":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)","_id":"@kilohealth/web-app-monitoring@1.3.0-alpha.1","_nodeVersion":"18.16.1","_npmVersion":"9.7.2","dist":{"integrity":"sha512-SMCJMD4SbZ2HJeddk6lB42NVs/hSa0j1Dg0IKMZJkUzLht4KE8/3SPZL0GMqKHIpaE4xibbRmJJaoQAiBzXVPA==","shasum":"bef426b6640ae1043f6383782275713ab093d476","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.3.0-alpha.1.tgz","fileCount":40,"unpackedSize":54857,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDpqBTrbnEJ9fLcYm5o4V4gbTNK2PlONKJsij41/uAjjgIhALZaQsbG9C1YcWEFSYDB60nYE5DfYS/ek3q2qpHoL8Zo"}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.3.0-alpha.1_1689311056417_0.41919458659949904"},"_hasShrinkwrap":false},"1.3.0-alpha.2":{"name":"@kilohealth/web-app-monitoring","version":"1.3.0-alpha.2","license":"MIT","author":{"name":"Kilo Health"},"sideEffects":false,"exports":{"./dist/server/initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./dist/server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./dist/browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","githooks:commit-msg":"commitlint --edit","githooks:pre-commit":"lint-staged","prepare":"husky install","semantic-release":"semantic-release","test":"jest"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","dd-trace":"^4.0.0","deepmerge":"^4.3.1","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","@types/lodash":"^4.14.195","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jest-environment-jsdom":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"readme":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)\n\n# @kilohealth/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\n## The Idea\n\nPackage was created to abstract away underlying level of monitoring,\nto make it easy to setup monitoring for any env as well as to make it easy to migrate from DataDog to other monitoring solution.\n\nThe package consists of 3 parts:\n\n- Browser / Client monitoring (browser logs)\n- CLI (needed to upload sourcemaps for browser monitoring)\n- Server monitoring (server logs, APM, tracing)\n\n## Getting Started\n\n> **Note:** If you are migrating from direct datadog integration - don’t forget to remove `@datadog/...` dependencies. Those are now dependencies of `@kilohealth/web-app-monitoring`.\n>\n> ```\n> npm uninstall @datadog/...\n> ```\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n### Setup environment variables\n\n| Variable                           | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              | Upload source maps | Server (APM, tracing) | Browser / Client |\n| ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------: | :-------------------: | :--------------: |\n| `MONITORING_TOOL__API_KEY`         | This key is needed in order to uploaded source maps for browser monitoring, send server side (APM) logs and tracing info. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048).                                                                                                                                                                                                                                                                                                                                                                                 |         ✔️         |          ✔️           |                  |\n| `MONITORING_TOOL__SERVICE_NAME`    | The service name, for example: `timely-hand-web-funnel-app`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |         ✔️         |          ✔️           |        ✔️        |\n| `MONITORING_TOOL__SERVICE_VERSION` | The service version, for example: `$CI_COMMIT_SHA`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |         ✔️         |          ✔️           |        ✔️        |\n| `MONITORING_TOOL__SERVICE_ENV`     | The service environment, for example: `$CI_ENVIRONMENT_NAME`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |         ✔️         |          ✔️           |        ✔️        |\n| `MONITORING_TOOL__CLIENT_TOKEN`    | This token is needed in order to send browser monitoring logs. You can create or find client token [here](https://app.datadoghq.com/organization-settings/client-tokens).                                                                                                                                                                                                                                                                                                                                                                                                                                                                |         ️          |                       |        ✔️        |\n| `MONITORING_TOOL__PUBLIC_PATH`     | This is RELATIVE path, part of URL between domain (which can be different for different environments) and path to file itself. In other words - base path for all the assets within your application.<br/>You can think of this as kind of relative [Public Path](https://webpack.js.org/guides/public-path/). For example it can be `/` or `/static`.<br/>In other words this is common relative prefix for all your static files or / if there is none.<br/> - for Vite.js the default is `/`<br/> - for Next.js the default is `/_next/static/chunks` (!!! `_` instead of `.` in file system)<br/> - for Gatsby.js the default is `/` |         ✔️         |                       |                  |\n| `MONITORING_TOOL__BUILD_DIR`       | This should be RELATIVE path to your build directory. For example `./dist` or `./build`.</br>- for Vite.js default is `./dist`</br>- for Next.js default is `./.next/static/chunks`</br>- for Gatsby.js default is `./public`                                                                                                                                                                                                                                                                                                                                                                                                            |         ✔️         |                       |                  |\n\n> **Note:** Depending on the framework you are using, in order to expose environment variables to the client you may need to prefix the environment variables as mentioned below:\n>\n> - For Next.js, add the prefix `NEXT_PUBLIC_` to each variable. Refer to the [documentation](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#bundling-environment-variables-for-the-browser) for more details.\n> - For Gatsby.js, add the prefix `GATSBY_` to each variable. Refer to the [documentation](https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/#accessing-environment-variables-in-the-browser) for more details.\n> - For Vite.js, add the prefix `VITE_` to each variable. Refer to the [documentation](https://vitejs.dev/guide/env-and-mode.html) for more details.\n\n> **Tip:** By following Single Source of Truth principle you can reexport variables, needed for the client, in the build stage (Next.js example):\n>\n> ```\n> NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME\n> NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION=$MONITORING_TOOL__SERVICE_VERSION\n> NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV=$MONITORING_TOOL__SERVICE_ENV\n> ```\n\n### Setup browser monitoring\n\n#### Generate hidden source maps\n\nIn order to upload source maps into the monitoring service we need to include those source map files into our build.\nThis can be done by slightly altering the build phase bundler configuration of our app:\n\n<details>\n<summary>Next.js (next.config.js)</summary>\n\n```js\nmodule.exports = {\n  webpack: (config, context) => {\n    const isClient = !context.isServer;\n    const isProd = !context.dev;\n    const isSourcemapsUploadEnabled = Boolean(\n      process.env.MONITORING_TOOL__API_KEY,\n    );\n\n    // Generate source maps only for the client side production build\n    if (isClient && isProd && isSourcemapsUploadEnabled) {\n      return {\n        ...config,\n        // No reference. No source maps exposure to the client (browser).\n        // Hidden source maps generation only for error reporting purposes.\n        devtool: 'hidden-source-map',\n      };\n    }\n\n    return config;\n  },\n};\n```\n\nRefer to the [documentation](https://webpack.js.org/configuration/devtool/) for more details.\n\n</details>\n\n<details>\n<summary>Gatsby.js (gatsby-node.js)</summary>\n\n```js\nmodule.exports = {\n  onCreateWebpackConfig: ({ stage, actions }) => {\n    const isSourcemapsUploadEnabled = Boolean(\n      process.env.MONITORING_TOOL__API_KEY,\n    );\n    // build-javascript is prod build phase\n    if (stage === 'build-javascript' && isSourcemapsUploadEnabled) {\n      actions.setWebpackConfig({\n        // No reference. No source maps exposure to the client (browser).\n        // Hidden source maps generation only for error reporting purposes.\n        devtool: 'hidden-source-map',\n      });\n    }\n  },\n};\n```\n\nRefer to the [documentation](https://webpack.js.org/configuration/devtool/) for more details.\n\n</details>\n\n<details>\n<summary>Vite.js (vite.config.js)</summary>\n\n```js\nexport default defineConfig({\n  build: {\n    // No reference. No source maps exposure to the client (browser).\n    // Hidden source maps generation only for error reporting purposes.\n    sourcemap: process.env.MONITORING_TOOL__API_KEY ? 'hidden' : false,\n  },\n});\n```\n\nRefer to the [documentation](https://vitejs.dev/config/build-options.html#build-sourcemap) for more details.\n\n</details>\n\n> **Note:** We are using `hidden source maps` only for error reporting purposes.\n> That means our source maps are not exposed to the client\n> and there are no references to those source maps in our source code.\n\n#### Upload generated source maps\n\nIn order to upload generated source maps into the monitoring service, you should use `web-app-monitoring__upload-sourcemaps` bin, provided by `@kilohealth/web-app-monitoring` package.\n\nScript example for Next.js:\n\n```\n\"scripts\": {\n  \"upload:sourcemaps\": \"MONITORING_TOOL__BUILD_DIR=./.next/static/chunks MONITORING_TOOL__PUBLIC_PATH=/_next/static/chunks web-app-monitoring__upload-sourcemaps\",\n  ...\n},\n```\n\nAnd then your CI should run `upload:sourcemaps` script for the build that includes generated source maps.\n\n### Browser Monitoring Usage\n\n> **Important note:** There is no single entry point for package. You can't do something like `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring';`\n> Reason for that is to avoid bundling server-code into client bundle and vice versa. This structure will ensure effective tree shaking during build time.\n\n> In case your bundler supports package.json `exports` field - you can also omit `dist` in path folder `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/browser';`\n\n```ts\nimport { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/dist/browser';\n\nexport const monitoring = new BrowserMonitoringService({\n  authToken: NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN,\n  serviceName: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV,\n});\n```\n\nAs you can see we are using here all our exposed variables. If any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n**OPTIONAL: If you are using React you may benefit from utilizing [Error Boundaries](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary):**\n\n```tsx\nimport React, { Component, PropsWithChildren } from 'react';\nimport { monitoring } from '../services/monitoring';\n\ninterface ErrorBoundaryProps {}\ninterface ErrorBoundaryState {\n  hasError: boolean;\n}\n\nexport class ErrorBoundary extends Component<\n  PropsWithChildren<ErrorBoundaryProps>,\n  ErrorBoundaryState\n> {\n  static getDerivedStateFromError(_: Error): ErrorBoundaryState {\n    return { hasError: true };\n  }\n\n  state: ErrorBoundaryState = {\n    hasError: false,\n  };\n\n  componentDidCatch(error: Error) {\n    monitoring.reportError(error);\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return <h1>Sorry... There was an error</h1>;\n    }\n\n    return this.props.children;\n  }\n}\n```\n\n### Setup Server Monitoring (Next.js)\n\n<details>\n<summary>Approach with facade</summary>\n\n`initServerMonitoring` is a facade over `ServerMonitoringService`.\nYou can instantiate and use that service directly.\nThis function basically does one thing - instantiate it and can do 3 more additional things:\n\n- call `overrideNativeConsole` - method of the service to override native console, to log to datadog instead.\n- cal `catchProcessErrors` - method of the service to subscribe to native errors, to log them to datadog.\n- put service itself into global scope under defined name, so serverside code can use it.\n\nYou may wonder why we instantiate service here and not in server-side code.\nThe reason for that is if we override the native console and catch native errors - we would like to set up this as soon as possible.\nIf you don’t care too much about the very first seconds of next server - you can use alternative simpler server side logging solution.\n\nExample for Next.js:\n\n- update next.config.ts to include into start script of production server code\n  `next.config.ts:`\n\n```js\nconst {\n  initServerMonitoring,\n} = require('@kilohealth/web-app-monitoring/dist/server');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    const remoteMonitoringServiceParams = {\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    };\n    const config = {\n      shouldOverrideNativeConsole: true,\n      shouldCatchProcessErrors: true,\n      globalMonitoringInstanceName: 'kiloServerMonitoring',\n    };\n    initServerMonitoring(remoteMonitoringServiceParams, config);\n  }\n};\n```\n\n- update `custom.d.ts` file to declare that global scope now have monitoring service as a prop\n  In order to use ServerMonitoringService instance in other parts of code via global we need to let TS know\n  that we added new property to global object.\n  In Next.js you can just create or add next code into `custom.d.ts` file in root of the project.\n  Be aware that var name matches string that you provided in code above (kiloServerMonitoring in this case).\n  `custom.d.ts:`\n\n```ts\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\ndeclare global {\n  // eslint-disable-next-line no-var\n  var kiloServerMonitoring: ServerMonitoringService;\n}\n```\n\n- use it in code\n\n```ts\nexport const getHomeServerSideProps = async context => {\n  global.kiloServerMonitoring.info('getHomeServerSideProps called');\n};\n```\n\n</details>\n\n<details>\n<summary>Approach with direct instantiation</summary>\n\nIf you don’t care too much about catching native errors or native logs\nin the early stages of your server app -\nyou can avoid sharing logger via global scope and instead initialize it inside of app.\n\n```ts\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\nexport const monitoring = new ServerMonitoringService({\n  authToken: MONITORING_TOOL__API_KEY,\n  serviceName: MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: MONITORING_TOOL__SERVICE_ENV,\n});\n```\n\nAs you can see we are using here all our env variables.\nIf any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n</details>\n\n#### Init Tracing\n\nWe need to connect tracing as soon as possible during code, so it can be injected into all base modules for APM monitoring.\nTracing module is available via:\n\n```js\nconst {\n  initTracing,\n} = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\n\ninitTracing({\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n});\n```\n\nExample for Next.js:\n\n```js\nconst { PHASE_PRODUCTION_SERVER } = require('next/constants');\nconst {\n  initTracing,\n} = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    initTracing({\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    });\n  }\n};\n```\n\n> **Note:** In newer versions of Next.js there is experimental feature called\n> [instrumentationHook](https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation)\n> We can opt out from using undocumented `PHASE_PRODUCTION_SERVER` to use `instrumentationHook` for tracing init.\n> There is also possibility to use `NODE_OPTIONS='-r ./prestart-script.js ' next start` instead.\n> But there is an issue with `pino-datadog-transport`, which for performance reason spawns separate thread for log sending to data-dog and it this option seems to be passed to that process as well which triggers an infinite loop of require and initialization.\n\n## API\n\n### MonitoringService (both BrowserMonitoringService and ServerMonitoringService have these methods)\n\n<details>\n<summary>debug, info, warn</summary>\n\n```\ndebug(message: string, context?: object)\ninfo(message: string, context?: object)\nwarn(message: string, context?: object)\n```\n\n- `message` - any message to be logged\n- `context` - object with all needed and related to the log entrance data\n</details>\n\n<details>\n<summary>error</summary>\n\n```\nerror(message: string, context?: object, error?: Error)\n```\n\nSame as above, but you can also optionally pass error instance as third parameter\n\n</details>\n\n<details>\n<summary>reportError</summary>\n\n```\nreportError(error: Error, context?: object)\n```\n\nShortcut for `service.error()`, which uses `error.message` field as message param for `error` method.\n\n</details>\n\n### BrowserMonitoringService\n\n<details>\n<summary>constructor</summary>\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```ts\ninterface RemoteMonitoringServiceParams {\n  serviceName?: string;\n  serviceVersion?: string;\n  serviceEnv?: string;\n  authToken?: string;\n}\n```\n\n- `RemoteMonitoringServiceConfig` - datadog params passed to init function. More info in [docs](https://docs.datadoghq.com/logs/log_collection/javascript/#initialization-parameters)\n- `serviceName` - name of the service\n- `serviceVersion` - version of the service\n- `serviceEnv` - environment where service is deployed\n- `authToken` - client token\n</details>\n\n### ServerMonitoringService\n\n<details>\n<summary>constructor</summary>\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```ts\ninterface RemoteMonitoringServiceConfig {\n  transportOptions?: Partial<TransportBaseOptions>;\n  loggerOptions?: Partial<LoggerOptions>;\n}\n```\n\n- `transportOptions` - [pino-datadog-transport options](https://github.com/theogravity/pino-datadog-transport#configuration-options)\n- `loggerOptions` - [pino logger options](https://getpino.io/#/docs/api?id=options-object)\n</details>\n\n<details>\n<summary>overrideLogger</summary>\n\nOverrides logger passed as argument with monitoring logger.\nAll methods of this logger will be overridden with corresponding methods of server monitoring.\n\n```\noverrideLogger(unknownLogger: UnknownLogger)\n```\n\n```ts\ninterface UnknownLogger {\n  log?(...parts: unknown[]): void;\n  debug?(...parts: unknown[]): void;\n  info(...parts: unknown[]): void;\n  warn(...parts: unknown[]): void;\n  error(...parts: unknown[]): void;\n}\n```\n\n</details>\n\n<details>\n<summary>overrideNativeConsole</summary>\n\nCalls overrideLogger for native console.\n\n```\noverrideNativeConsole()\n```\n\n</details>\n\n<details>\n<summary>catchProcessErrors</summary>\n\nSubscribes to `unhandledRejection` and `uncaughtException` events of the process to report `error` in such cases.\n\n```ts\ncatchProcessErrors();\n```\n\n</details>\n\n### ServerMonitoringService\n\n<details>\n<summary>initServerMonitoring</summary>\n\nInstantiate ServerMonitoringService with provided params and may also do additional work,\ndepending on provided variables.\n\n```\ninitServerMonitoring = (\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  monitoringOptions?: MonitoringOptions,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig\n): ServerMonitoringService\n```\n\n```ts\ninterface MonitoringOptions {\n  shouldOverrideNativeConsole?: boolean;\n  shouldCatchProcessErrors?: boolean;\n  globalMonitoringInstanceName?: string;\n}\n```\n\n- `RemoteMonitoringServiceParams` and `RemoteMonitoringServiceConfig` are same as in `constructor` api\n- `shouldOverrideNativeConsole` - if `true`, will call `serverMonitoringService.overrideNativeConsole()` under the hood\n- `shouldCatchProcessErrors` - if `true`, will call `serverMonitoringService.catchProcessErrors()` under the hood\n- `globalMonitoringInstanceName` - if provided with non-empty string will put instantiated `serverMonitoringService` into global scope under provided name.\n\n</details>\n","readmeFilename":"README.md","gitHead":"8a7b7177d9301e4f7e927fffc31243131fb2242a","description":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)","_id":"@kilohealth/web-app-monitoring@1.3.0-alpha.2","_nodeVersion":"18.16.1","_npmVersion":"9.7.2","dist":{"integrity":"sha512-zyL312fS69J9dv3RLwVfqNq87yl/e2Voa7G+NlE65oeK27ll3QJMw7WBUyPSrqBLB7DbI42ANxhFacpyAuNFPQ==","shasum":"2c769961ce85c440dc20b52a6c15fc9487cc550f","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-1.3.0-alpha.2.tgz","fileCount":40,"unpackedSize":56606,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH8r7AUQEGy2xT7x8Dns3AiMr5NVNlmcbh3mcqZwHm+qAiEAh8MXroRvCHw+xsxBZOPVuuL6+bahg0W5FgH8R4RYU3k="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_1.3.0-alpha.2_1689311322395_0.3275067110302903"},"_hasShrinkwrap":false},"2.0.0-alpha.1":{"name":"@kilohealth/web-app-monitoring","version":"2.0.0-alpha.1","license":"MIT","author":{"name":"Kilo Health"},"sideEffects":false,"exports":{"./dist/server/initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./dist/server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./dist/browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","githooks:commit-msg":"commitlint --edit","githooks:pre-commit":"lint-staged","prepare":"husky install","semantic-release":"semantic-release","test":"jest"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","dd-trace":"^4.0.0","deepmerge":"^4.3.1","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","@types/lodash":"^4.14.195","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jest-environment-jsdom":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"readme":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)\n\n# @kilohealth/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\n## The Idea\n\nPackage was created to abstract away underlying level of monitoring,\nto make it easy to setup monitoring for any env as well as to make it easy to migrate from DataDog to other monitoring solution.\n\nThe package consists of 3 parts:\n\n- Browser / Client monitoring (browser logs)\n- CLI (needed to upload sourcemaps for browser monitoring)\n- Server monitoring (server logs, APM, tracing)\n\n## Getting Started\n\n> **Note:** If you are migrating from direct datadog integration - don’t forget to remove `@datadog/...` dependencies. Those are now dependencies of `@kilohealth/web-app-monitoring`.\n>\n> ```\n> npm uninstall @datadog/...\n> ```\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n### Setup environment variables\n\n| Variable                           | Description                                                                                                                                                                                                                                              | Upload source maps | Server (APM, tracing) | Browser / Client |\n| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------: | :-------------------: | :--------------: |\n| `MONITORING_TOOL__API_KEY`         | This key is needed in order to uploaded source maps for browser monitoring, send server side (APM) logs and tracing info. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048). |         ✔️         |          ✔️           |                  |\n| `MONITORING_TOOL__SERVICE_NAME`    | The service name, for example: `timely-hand-web-funnel-app`.                                                                                                                                                                                             |         ✔️         |          ✔️           |        ✔️        |\n| `MONITORING_TOOL__SERVICE_VERSION` | The service version, for example: `$CI_COMMIT_SHA`.                                                                                                                                                                                                      |         ✔️         |          ✔️           |        ✔️        |\n| `MONITORING_TOOL__SERVICE_ENV`     | The service environment, for example: `$CI_ENVIRONMENT_NAME`.                                                                                                                                                                                            |         ✔️         |          ✔️           |        ✔️        |\n| `MONITORING_TOOL__CLIENT_TOKEN`    | This token is needed in order to send browser monitoring logs. You can create or find client token [here](https://app.datadoghq.com/organization-settings/client-tokens).                                                                                |         ️          |                       |        ✔️        |\n\n> **Note:** Depending on the framework you are using, in order to expose environment variables to the client you may need to prefix the environment variables as mentioned below:\n>\n> - For Next.js, add the prefix `NEXT_PUBLIC_` to each variable. Refer to the [documentation](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#bundling-environment-variables-for-the-browser) for more details.\n> - For Gatsby.js, add the prefix `GATSBY_` to each variable. Refer to the [documentation](https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/#accessing-environment-variables-in-the-browser) for more details.\n> - For Vite.js, add the prefix `VITE_` to each variable. Refer to the [documentation](https://vitejs.dev/guide/env-and-mode.html) for more details.\n\n> **Tip:** By following Single Source of Truth principle you can reexport variables, needed for the client, in the build stage (Next.js example):\n>\n> ```\n> NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME\n> NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION=$MONITORING_TOOL__SERVICE_VERSION\n> NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV=$MONITORING_TOOL__SERVICE_ENV\n> ```\n\n### Setup browser monitoring\n\n#### Generate hidden source maps\n\nIn order to upload source maps into the monitoring service we need to include those source map files into our build.\nThis can be done by slightly altering the build phase bundler configuration of our app:\n\n<details>\n<summary>Next.js (next.config.js)</summary>\n\n```js\nmodule.exports = {\n  webpack: (config, context) => {\n    const isClient = !context.isServer;\n    const isProd = !context.dev;\n    const isSourcemapsUploadEnabled = Boolean(\n      process.env.MONITORING_TOOL__API_KEY,\n    );\n\n    // Generate source maps only for the client side production build\n    if (isClient && isProd && isSourcemapsUploadEnabled) {\n      return {\n        ...config,\n        // No reference. No source maps exposure to the client (browser).\n        // Hidden source maps generation only for error reporting purposes.\n        devtool: 'hidden-source-map',\n      };\n    }\n\n    return config;\n  },\n};\n```\n\nRefer to the [documentation](https://webpack.js.org/configuration/devtool/) for more details.\n\n</details>\n\n<details>\n<summary>Gatsby.js (gatsby-node.js)</summary>\n\n```js\nmodule.exports = {\n  onCreateWebpackConfig: ({ stage, actions }) => {\n    const isSourcemapsUploadEnabled = Boolean(\n      process.env.MONITORING_TOOL__API_KEY,\n    );\n    // build-javascript is prod build phase\n    if (stage === 'build-javascript' && isSourcemapsUploadEnabled) {\n      actions.setWebpackConfig({\n        // No reference. No source maps exposure to the client (browser).\n        // Hidden source maps generation only for error reporting purposes.\n        devtool: 'hidden-source-map',\n      });\n    }\n  },\n};\n```\n\nRefer to the [documentation](https://webpack.js.org/configuration/devtool/) for more details.\n\n</details>\n\n<details>\n<summary>Vite.js (vite.config.js)</summary>\n\n```js\nexport default defineConfig({\n  build: {\n    // No reference. No source maps exposure to the client (browser).\n    // Hidden source maps generation only for error reporting purposes.\n    sourcemap: process.env.MONITORING_TOOL__API_KEY ? 'hidden' : false,\n  },\n});\n```\n\nRefer to the [documentation](https://vitejs.dev/config/build-options.html#build-sourcemap) for more details.\n\n</details>\n\n> **Note:** We are using `hidden source maps` only for error reporting purposes.\n> That means our source maps are not exposed to the client\n> and there are no references to those source maps in our source code.\n\n#### Upload generated source maps\n\nIn order to upload generated source maps into the monitoring service, you should use `web-app-monitoring__upload-sourcemaps` bin, provided by `@kilohealth/web-app-monitoring` package.\nTo run the script you need to provide arguments:\n\n| Argument               | Description                                                                                                                                                                                                                                                                                                                                                                                                                                             |   Vite   |                              Next                               |   Gatsby   |\n| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------: | :-------------------------------------------------------------: | :--------: |\n| `--buildDir` or `-d`   | This should be RELATIVE path to your build directory. For example `./dist` or `./build`.                                                                                                                                                                                                                                                                                                                                                                | `./dist` |                     `./.next/static/chunks`                     | `./public` |\n| `--publicPath` or `-p` | This is RELATIVE path, part of URL between domain (which can be different for different environments) and path to file itself. In other words - base path for all the assets within your application.<br/>You can think of this as kind of relative [Public Path](https://webpack.js.org/guides/public-path/). For example it can be `/` or `/static`. In other words this is common relative prefix for all your static files or `/` if there is none. |   `/`    | `/_next/static/chunks` (!!! `_` instead of `.` in file system)️ |    `/`     |\n\nScript example for Next.js:\n\n```\n\"scripts\": {\n  \"upload:sourcemaps\": \"web-app-monitoring__upload-sourcemaps --buildDir=./.next/static/chunks --publicPath=/_next/static/chunks\",\n  ...\n},\n```\n\nAnd then your CI should run `upload:sourcemaps` script for the build that includes generated source maps.\n\n### Browser Monitoring Usage\n\n> **Important note:** There is no single entry point for package. You can't do something like `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring';`\n> Reason for that is to avoid bundling server-code into client bundle and vice versa. This structure will ensure effective tree shaking during build time.\n\n> In case your bundler supports package.json `exports` field - you can also omit `dist` in path folder `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/browser';`\n\n```ts\nimport { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/dist/browser';\n\nexport const monitoring = new BrowserMonitoringService({\n  authToken: NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN,\n  serviceName: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV,\n});\n```\n\nAs you can see we are using here all our exposed variables. If any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n**OPTIONAL: If you are using React you may benefit from utilizing [Error Boundaries](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary):**\n\n```tsx\nimport React, { Component, PropsWithChildren } from 'react';\nimport { monitoring } from '../services/monitoring';\n\ninterface ErrorBoundaryProps {}\ninterface ErrorBoundaryState {\n  hasError: boolean;\n}\n\nexport class ErrorBoundary extends Component<\n  PropsWithChildren<ErrorBoundaryProps>,\n  ErrorBoundaryState\n> {\n  static getDerivedStateFromError(_: Error): ErrorBoundaryState {\n    return { hasError: true };\n  }\n\n  state: ErrorBoundaryState = {\n    hasError: false,\n  };\n\n  componentDidCatch(error: Error) {\n    monitoring.reportError(error);\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return <h1>Sorry... There was an error</h1>;\n    }\n\n    return this.props.children;\n  }\n}\n```\n\n### Setup Server Monitoring (Next.js)\n\n<details>\n<summary>Approach with facade</summary>\n\n`initServerMonitoring` is a facade over `ServerMonitoringService`.\nYou can instantiate and use that service directly.\nThis function basically does one thing - instantiate it and can do 3 more additional things:\n\n- call `overrideNativeConsole` - method of the service to override native console, to log to datadog instead.\n- cal `catchProcessErrors` - method of the service to subscribe to native errors, to log them to datadog.\n- put service itself into global scope under defined name, so serverside code can use it.\n\nYou may wonder why we instantiate service here and not in server-side code.\nThe reason for that is if we override the native console and catch native errors - we would like to set up this as soon as possible.\nIf you don’t care too much about the very first seconds of next server - you can use alternative simpler server side logging solution.\n\nExample for Next.js:\n\n- update next.config.ts to include into start script of production server code\n  `next.config.ts:`\n\n```js\nconst {\n  initServerMonitoring,\n} = require('@kilohealth/web-app-monitoring/dist/server');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    const remoteMonitoringServiceParams = {\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    };\n    const config = {\n      shouldOverrideNativeConsole: true,\n      shouldCatchProcessErrors: true,\n      globalMonitoringInstanceName: 'kiloServerMonitoring',\n    };\n    initServerMonitoring(remoteMonitoringServiceParams, config);\n  }\n};\n```\n\n- update `custom.d.ts` file to declare that global scope now have monitoring service as a prop\n  In order to use ServerMonitoringService instance in other parts of code via global we need to let TS know\n  that we added new property to global object.\n  In Next.js you can just create or add next code into `custom.d.ts` file in root of the project.\n  Be aware that var name matches string that you provided in code above (kiloServerMonitoring in this case).\n  `custom.d.ts:`\n\n```ts\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\ndeclare global {\n  // eslint-disable-next-line no-var\n  var kiloServerMonitoring: ServerMonitoringService;\n}\n```\n\n- use it in code\n\n```ts\nexport const getHomeServerSideProps = async context => {\n  global.kiloServerMonitoring.info('getHomeServerSideProps called');\n};\n```\n\n</details>\n\n<details>\n<summary>Approach with direct instantiation</summary>\n\nIf you don’t care too much about catching native errors or native logs\nin the early stages of your server app -\nyou can avoid sharing logger via global scope and instead initialize it inside of app.\n\n```ts\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\nexport const monitoring = new ServerMonitoringService({\n  authToken: MONITORING_TOOL__API_KEY,\n  serviceName: MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: MONITORING_TOOL__SERVICE_ENV,\n});\n```\n\nAs you can see we are using here all our env variables.\nIf any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n</details>\n\n#### Init Tracing\n\nWe need to connect tracing as soon as possible during code, so it can be injected into all base modules for APM monitoring.\nTracing module is available via:\n\n```js\nconst {\n  initTracing,\n} = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\n\ninitTracing({\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n});\n```\n\nExample for Next.js:\n\n```js\nconst { PHASE_PRODUCTION_SERVER } = require('next/constants');\nconst {\n  initTracing,\n} = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    initTracing({\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    });\n  }\n};\n```\n\n> **Note:** In newer versions of Next.js there is experimental feature called\n> [instrumentationHook](https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation)\n> We can opt out from using undocumented `PHASE_PRODUCTION_SERVER` to use `instrumentationHook` for tracing init.\n> There is also possibility to use `NODE_OPTIONS='-r ./prestart-script.js ' next start` instead.\n> But there is an issue with `pino-datadog-transport`, which for performance reason spawns separate thread for log sending to data-dog and it this option seems to be passed to that process as well which triggers an infinite loop of require and initialization.\n\n## API\n\n### MonitoringService (both BrowserMonitoringService and ServerMonitoringService have these methods)\n\n<details>\n<summary>debug, info, warn</summary>\n\n```\ndebug(message: string, context?: object)\ninfo(message: string, context?: object)\nwarn(message: string, context?: object)\n```\n\n- `message` - any message to be logged\n- `context` - object with all needed and related to the log entrance data\n</details>\n\n<details>\n<summary>error</summary>\n\n```\nerror(message: string, context?: object, error?: Error)\n```\n\nSame as above, but you can also optionally pass error instance as third parameter\n\n</details>\n\n<details>\n<summary>reportError</summary>\n\n```\nreportError(error: Error, context?: object)\n```\n\nShortcut for `service.error()`, which uses `error.message` field as message param for `error` method.\n\n</details>\n\n### BrowserMonitoringService\n\n<details>\n<summary>constructor</summary>\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```ts\ninterface RemoteMonitoringServiceParams {\n  serviceName?: string;\n  serviceVersion?: string;\n  serviceEnv?: string;\n  authToken?: string;\n}\n```\n\n- `RemoteMonitoringServiceConfig` - datadog params passed to init function. More info in [docs](https://docs.datadoghq.com/logs/log_collection/javascript/#initialization-parameters)\n- `serviceName` - name of the service\n- `serviceVersion` - version of the service\n- `serviceEnv` - environment where service is deployed\n- `authToken` - client token\n</details>\n\n### ServerMonitoringService\n\n<details>\n<summary>constructor</summary>\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```ts\ninterface RemoteMonitoringServiceConfig {\n  transportOptions?: Partial<TransportBaseOptions>;\n  loggerOptions?: Partial<LoggerOptions>;\n}\n```\n\n- `transportOptions` - [pino-datadog-transport options](https://github.com/theogravity/pino-datadog-transport#configuration-options)\n- `loggerOptions` - [pino logger options](https://getpino.io/#/docs/api?id=options-object)\n</details>\n\n<details>\n<summary>overrideLogger</summary>\n\nOverrides logger passed as argument with monitoring logger.\nAll methods of this logger will be overridden with corresponding methods of server monitoring.\n\n```\noverrideLogger(unknownLogger: UnknownLogger)\n```\n\n```ts\ninterface UnknownLogger {\n  log?(...parts: unknown[]): void;\n  debug?(...parts: unknown[]): void;\n  info(...parts: unknown[]): void;\n  warn(...parts: unknown[]): void;\n  error(...parts: unknown[]): void;\n}\n```\n\n</details>\n\n<details>\n<summary>overrideNativeConsole</summary>\n\nCalls overrideLogger for native console.\n\n```\noverrideNativeConsole()\n```\n\n</details>\n\n<details>\n<summary>catchProcessErrors</summary>\n\nSubscribes to `unhandledRejection` and `uncaughtException` events of the process to report `error` in such cases.\n\n```ts\ncatchProcessErrors();\n```\n\n</details>\n\n### ServerMonitoringService\n\n<details>\n<summary>initServerMonitoring</summary>\n\nInstantiate ServerMonitoringService with provided params and may also do additional work,\ndepending on provided variables.\n\n```\ninitServerMonitoring = (\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  monitoringOptions?: MonitoringOptions,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig\n): ServerMonitoringService\n```\n\n```ts\ninterface MonitoringOptions {\n  shouldOverrideNativeConsole?: boolean;\n  shouldCatchProcessErrors?: boolean;\n  globalMonitoringInstanceName?: string;\n}\n```\n\n- `RemoteMonitoringServiceParams` and `RemoteMonitoringServiceConfig` are same as in `constructor` api\n- `shouldOverrideNativeConsole` - if `true`, will call `serverMonitoringService.overrideNativeConsole()` under the hood\n- `shouldCatchProcessErrors` - if `true`, will call `serverMonitoringService.catchProcessErrors()` under the hood\n- `globalMonitoringInstanceName` - if provided with non-empty string will put instantiated `serverMonitoringService` into global scope under provided name.\n\n</details>\n","readmeFilename":"README.md","gitHead":"dc4680d329158795d3e771c7c07367a97f1c92d9","description":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)","_id":"@kilohealth/web-app-monitoring@2.0.0-alpha.1","_nodeVersion":"18.16.1","_npmVersion":"9.7.2","dist":{"integrity":"sha512-0DtxSgW7FPC0l7AnpAV9xK5mPRzi6CJwqajLbdR4lIx8ipidqBhib0N4uKUE4mSKXK7GI9GATxbPFfxW3D2pcA==","shasum":"9556f50306d0eeb1ed8e8d1cc6b44d0b13a2dd9c","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-2.0.0-alpha.1.tgz","fileCount":40,"unpackedSize":54913,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAsk9x8remAm3t3ZbwLtR9CPApblqyho+x0sVDCICiXlAiEA8+w5AiUKRVq0KBie0jPFruxcwI7qJErhh5VWqh0m2Ec="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_2.0.0-alpha.1_1689316008399_0.1239160743649288"},"_hasShrinkwrap":false},"2.0.0":{"name":"@kilohealth/web-app-monitoring","version":"2.0.0","license":"MIT","author":{"name":"Kilo Health"},"sideEffects":false,"exports":{"./dist/server/initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./dist/server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./dist/browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","githooks:commit-msg":"commitlint --edit","githooks:pre-commit":"lint-staged","prepare":"husky install","semantic-release":"semantic-release","test":"jest"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","dd-trace":"^4.0.0","deepmerge":"^4.3.1","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","@types/lodash":"^4.14.195","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jest-environment-jsdom":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"gitHead":"ab668fac41e2b644da2e97c0e8917faf546d1e0e","description":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)","_id":"@kilohealth/web-app-monitoring@2.0.0","_nodeVersion":"18.16.1","_npmVersion":"9.7.2","dist":{"integrity":"sha512-nkkSgrdF0Ic4KeKuSrXV0Z+VEBoUwBat8zo+d2cMUvyS0szztbIO4/qYNd4eDL3ACGP2DL4Qy/QszYIIBawMxg==","shasum":"9e0114296f832bb37d24120581aeea56f0a7f240","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-2.0.0.tgz","fileCount":40,"unpackedSize":54905,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDcSK+GkQsQ7x5JDUKPFBtdYbq9gO4w6H18Bb1I7NdJdgIhAObrOQ+qjRKOlu8F5uaUYqapUl84GEJZV/l+wf/e7uyJ"}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_2.0.0_1689317979891_0.16731752043120318"},"_hasShrinkwrap":false},"2.0.1-alpha.1":{"name":"@kilohealth/web-app-monitoring","version":"2.0.1-alpha.1","license":"MIT","author":{"name":"Kilo Health"},"sideEffects":false,"exports":{"./dist/server/initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./dist/server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./dist/browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","githooks:commit-msg":"commitlint --edit","githooks:pre-commit":"lint-staged","prepare":"husky install","semantic-release":"semantic-release","test":"jest"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","dd-trace":"^4.0.0","deepmerge":"^4.3.1","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","@types/lodash":"^4.14.195","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jest-environment-jsdom":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"readme":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)\n\n# @kilohealth/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\n## The Idea\n\nPackage was created to abstract away underlying level of monitoring,\nto make it easy to setup monitoring for any env as well as to make it easy to migrate from DataDog to other monitoring solution.\n\nThe package consists of 3 parts:\n\n- Browser / Client monitoring (browser logs)\n- CLI (needed to upload sourcemaps for browser monitoring)\n- Server monitoring (server logs, APM, tracing)\n\n## Getting Started\n\n> **Note:** If you are migrating from direct datadog integration - don’t forget to remove `@datadog/...` dependencies. Those are now dependencies of `@kilohealth/web-app-monitoring`.\n>\n> ```\n> npm uninstall @datadog/...\n> ```\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n### Setup environment variables\n\n| Variable                           | Description                                                                                                                                                                                                                                              | Upload source maps | Server (APM, tracing) | Browser / Client |\n| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------: | :-------------------: | :--------------: |\n| `MONITORING_TOOL__API_KEY`         | This key is needed in order to uploaded source maps for browser monitoring, send server side (APM) logs and tracing info. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048). |         ✔️         |          ✔️           |                  |\n| `MONITORING_TOOL__SERVICE_NAME`    | The service name, for example: `timely-hand-web-funnel-app`.                                                                                                                                                                                             |         ✔️         |          ✔️           |        ✔️        |\n| `MONITORING_TOOL__SERVICE_VERSION` | The service version, for example: `$CI_COMMIT_SHA`.                                                                                                                                                                                                      |         ✔️         |          ✔️           |        ✔️        |\n| `MONITORING_TOOL__SERVICE_ENV`     | The service environment, for example: `$CI_ENVIRONMENT_NAME`.                                                                                                                                                                                            |         ✔️         |          ✔️           |        ✔️        |\n| `MONITORING_TOOL__CLIENT_TOKEN`    | This token is needed in order to send browser monitoring logs. You can create or find client token [here](https://app.datadoghq.com/organization-settings/client-tokens).                                                                                |         ️          |                       |        ✔️        |\n\n> **Note:** Depending on the framework you are using, in order to expose environment variables to the client you may need to prefix the environment variables as mentioned below:\n>\n> - For Next.js, add the prefix `NEXT_PUBLIC_` to each variable. Refer to the [documentation](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#bundling-environment-variables-for-the-browser) for more details.\n> - For Gatsby.js, add the prefix `GATSBY_` to each variable. Refer to the [documentation](https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/#accessing-environment-variables-in-the-browser) for more details.\n> - For Vite.js, add the prefix `VITE_` to each variable. Refer to the [documentation](https://vitejs.dev/guide/env-and-mode.html) for more details.\n\n> **Tip:** By following Single Source of Truth principle you can reexport variables, needed for the client, in the build stage (Next.js example):\n>\n> ```\n> NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME\n> NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION=$MONITORING_TOOL__SERVICE_VERSION\n> NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV=$MONITORING_TOOL__SERVICE_ENV\n> ```\n\n### Setup browser monitoring\n\n#### Generate hidden source maps\n\nIn order to upload source maps into the monitoring service we need to include those source map files into our build.\nThis can be done by slightly altering the build phase bundler configuration of our app:\n\n<details>\n<summary>Next.js (next.config.js)</summary>\n\n```js\nmodule.exports = {\n  webpack: (config, context) => {\n    const isClient = !context.isServer;\n    const isProd = !context.dev;\n    const isSourcemapsUploadEnabled = Boolean(\n      process.env.MONITORING_TOOL__API_KEY,\n    );\n\n    // Generate source maps only for the client side production build\n    if (isClient && isProd && isSourcemapsUploadEnabled) {\n      return {\n        ...config,\n        // No reference. No source maps exposure to the client (browser).\n        // Hidden source maps generation only for error reporting purposes.\n        devtool: 'hidden-source-map',\n      };\n    }\n\n    return config;\n  },\n};\n```\n\nRefer to the [documentation](https://webpack.js.org/configuration/devtool/) for more details.\n\n</details>\n\n<details>\n<summary>Gatsby.js (gatsby-node.js)</summary>\n\n```js\nmodule.exports = {\n  onCreateWebpackConfig: ({ stage, actions }) => {\n    const isSourcemapsUploadEnabled = Boolean(\n      process.env.MONITORING_TOOL__API_KEY,\n    );\n    // build-javascript is prod build phase\n    if (stage === 'build-javascript' && isSourcemapsUploadEnabled) {\n      actions.setWebpackConfig({\n        // No reference. No source maps exposure to the client (browser).\n        // Hidden source maps generation only for error reporting purposes.\n        devtool: 'hidden-source-map',\n      });\n    }\n  },\n};\n```\n\nRefer to the [documentation](https://webpack.js.org/configuration/devtool/) for more details.\n\n</details>\n\n<details>\n<summary>Vite.js (vite.config.js)</summary>\n\n```js\nexport default defineConfig({\n  build: {\n    // No reference. No source maps exposure to the client (browser).\n    // Hidden source maps generation only for error reporting purposes.\n    sourcemap: process.env.MONITORING_TOOL__API_KEY ? 'hidden' : false,\n  },\n});\n```\n\nRefer to the [documentation](https://vitejs.dev/config/build-options.html#build-sourcemap) for more details.\n\n</details>\n\n> **Note:** We are using `hidden source maps` only for error reporting purposes.\n> That means our source maps are not exposed to the client\n> and there are no references to those source maps in our source code.\n\n#### Upload generated source maps\n\nIn order to upload generated source maps into the monitoring service, you should use `web-app-monitoring__upload-sourcemaps` bin, provided by `@kilohealth/web-app-monitoring` package.\nTo run the script you need to provide arguments:\n\n| Argument               | Description                                                                                                                                                                                                                                                                                                                                                                                                                                             |   Vite   |                              Next                               |   Gatsby   |\n| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------: | :-------------------------------------------------------------: | :--------: |\n| `--buildDir` or `-d`   | This should be RELATIVE path to your build directory. For example `./dist` or `./build`.                                                                                                                                                                                                                                                                                                                                                                | `./dist` |                     `./.next/static/chunks`                     | `./public` |\n| `--publicPath` or `-p` | This is RELATIVE path, part of URL between domain (which can be different for different environments) and path to file itself. In other words - base path for all the assets within your application.<br/>You can think of this as kind of relative [Public Path](https://webpack.js.org/guides/public-path/). For example it can be `/` or `/static`. In other words this is common relative prefix for all your static files or `/` if there is none. |   `/`    | `/_next/static/chunks` (!!! `_` instead of `.` in file system)️ |    `/`     |\n\nScript example for Next.js:\n\n```\n\"scripts\": {\n  \"upload:sourcemaps\": \"web-app-monitoring__upload-sourcemaps --buildDir ./.next/static/chunks --publicPath /_next/static/chunks\",\n  ...\n},\n```\n\nAnd then your CI should run `upload:sourcemaps` script for the build that includes generated source maps.\n\n### Browser Monitoring Usage\n\n> **Important note:** There is no single entry point for package. You can't do something like `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring';`\n> Reason for that is to avoid bundling server-code into client bundle and vice versa. This structure will ensure effective tree shaking during build time.\n\n> In case your bundler supports package.json `exports` field - you can also omit `dist` in path folder `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/browser';`\n\n```ts\nimport { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/dist/browser';\n\nexport const monitoring = new BrowserMonitoringService({\n  authToken: NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN,\n  serviceName: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV,\n});\n```\n\nAs you can see we are using here all our exposed variables. If any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n**OPTIONAL: If you are using React you may benefit from utilizing [Error Boundaries](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary):**\n\n```tsx\nimport React, { Component, PropsWithChildren } from 'react';\nimport { monitoring } from '../services/monitoring';\n\ninterface ErrorBoundaryProps {}\ninterface ErrorBoundaryState {\n  hasError: boolean;\n}\n\nexport class ErrorBoundary extends Component<\n  PropsWithChildren<ErrorBoundaryProps>,\n  ErrorBoundaryState\n> {\n  static getDerivedStateFromError(_: Error): ErrorBoundaryState {\n    return { hasError: true };\n  }\n\n  state: ErrorBoundaryState = {\n    hasError: false,\n  };\n\n  componentDidCatch(error: Error) {\n    monitoring.reportError(error);\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return <h1>Sorry... There was an error</h1>;\n    }\n\n    return this.props.children;\n  }\n}\n```\n\n### Setup Server Monitoring (Next.js)\n\n<details>\n<summary>Approach with facade</summary>\n\n`initServerMonitoring` is a facade over `ServerMonitoringService`.\nYou can instantiate and use that service directly.\nThis function basically does one thing - instantiate it and can do 3 more additional things:\n\n- call `overrideNativeConsole` - method of the service to override native console, to log to datadog instead.\n- cal `catchProcessErrors` - method of the service to subscribe to native errors, to log them to datadog.\n- put service itself into global scope under defined name, so serverside code can use it.\n\nYou may wonder why we instantiate service here and not in server-side code.\nThe reason for that is if we override the native console and catch native errors - we would like to set up this as soon as possible.\nIf you don’t care too much about the very first seconds of next server - you can use alternative simpler server side logging solution.\n\nExample for Next.js:\n\n- update next.config.ts to include into start script of production server code\n  `next.config.ts:`\n\n```js\nconst {\n  initServerMonitoring,\n} = require('@kilohealth/web-app-monitoring/dist/server');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    const remoteMonitoringServiceParams = {\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    };\n    const config = {\n      shouldOverrideNativeConsole: true,\n      shouldCatchProcessErrors: true,\n      globalMonitoringInstanceName: 'kiloServerMonitoring',\n    };\n    initServerMonitoring(remoteMonitoringServiceParams, config);\n  }\n};\n```\n\n- update `custom.d.ts` file to declare that global scope now have monitoring service as a prop\n  In order to use ServerMonitoringService instance in other parts of code via global we need to let TS know\n  that we added new property to global object.\n  In Next.js you can just create or add next code into `custom.d.ts` file in root of the project.\n  Be aware that var name matches string that you provided in code above (kiloServerMonitoring in this case).\n  `custom.d.ts:`\n\n```ts\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\ndeclare global {\n  // eslint-disable-next-line no-var\n  var kiloServerMonitoring: ServerMonitoringService;\n}\n```\n\n- use it in code\n\n```ts\nexport const getHomeServerSideProps = async context => {\n  global.kiloServerMonitoring.info('getHomeServerSideProps called');\n};\n```\n\n</details>\n\n<details>\n<summary>Approach with direct instantiation</summary>\n\nIf you don’t care too much about catching native errors or native logs\nin the early stages of your server app -\nyou can avoid sharing logger via global scope and instead initialize it inside of app.\n\n```ts\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\nexport const monitoring = new ServerMonitoringService({\n  authToken: MONITORING_TOOL__API_KEY,\n  serviceName: MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: MONITORING_TOOL__SERVICE_ENV,\n});\n```\n\nAs you can see we are using here all our env variables.\nIf any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n</details>\n\n#### Init Tracing\n\nWe need to connect tracing as soon as possible during code, so it can be injected into all base modules for APM monitoring.\nTracing module is available via:\n\n```js\nconst {\n  initTracing,\n} = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\n\ninitTracing({\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n});\n```\n\nExample for Next.js:\n\n```js\nconst { PHASE_PRODUCTION_SERVER } = require('next/constants');\nconst {\n  initTracing,\n} = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    initTracing({\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    });\n  }\n};\n```\n\n> **Note:** In newer versions of Next.js there is experimental feature called\n> [instrumentationHook](https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation)\n> We can opt out from using undocumented `PHASE_PRODUCTION_SERVER` to use `instrumentationHook` for tracing init.\n> There is also possibility to use `NODE_OPTIONS='-r ./prestart-script.js ' next start` instead.\n> But there is an issue with `pino-datadog-transport`, which for performance reason spawns separate thread for log sending to data-dog and it this option seems to be passed to that process as well which triggers an infinite loop of require and initialization.\n\n## API\n\n### MonitoringService (both BrowserMonitoringService and ServerMonitoringService have these methods)\n\n<details>\n<summary>debug, info, warn</summary>\n\n```\ndebug(message: string, context?: object)\ninfo(message: string, context?: object)\nwarn(message: string, context?: object)\n```\n\n- `message` - any message to be logged\n- `context` - object with all needed and related to the log entrance data\n</details>\n\n<details>\n<summary>error</summary>\n\n```\nerror(message: string, context?: object, error?: Error)\n```\n\nSame as above, but you can also optionally pass error instance as third parameter\n\n</details>\n\n<details>\n<summary>reportError</summary>\n\n```\nreportError(error: Error, context?: object)\n```\n\nShortcut for `service.error()`, which uses `error.message` field as message param for `error` method.\n\n</details>\n\n### BrowserMonitoringService\n\n<details>\n<summary>constructor</summary>\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```ts\ninterface RemoteMonitoringServiceParams {\n  serviceName?: string;\n  serviceVersion?: string;\n  serviceEnv?: string;\n  authToken?: string;\n}\n```\n\n- `RemoteMonitoringServiceConfig` - datadog params passed to init function. More info in [docs](https://docs.datadoghq.com/logs/log_collection/javascript/#initialization-parameters)\n- `serviceName` - name of the service\n- `serviceVersion` - version of the service\n- `serviceEnv` - environment where service is deployed\n- `authToken` - client token\n</details>\n\n### ServerMonitoringService\n\n<details>\n<summary>constructor</summary>\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```ts\ninterface RemoteMonitoringServiceConfig {\n  transportOptions?: Partial<TransportBaseOptions>;\n  loggerOptions?: Partial<LoggerOptions>;\n}\n```\n\n- `transportOptions` - [pino-datadog-transport options](https://github.com/theogravity/pino-datadog-transport#configuration-options)\n- `loggerOptions` - [pino logger options](https://getpino.io/#/docs/api?id=options-object)\n</details>\n\n<details>\n<summary>overrideLogger</summary>\n\nOverrides logger passed as argument with monitoring logger.\nAll methods of this logger will be overridden with corresponding methods of server monitoring.\n\n```\noverrideLogger(unknownLogger: UnknownLogger)\n```\n\n```ts\ninterface UnknownLogger {\n  log?(...parts: unknown[]): void;\n  debug?(...parts: unknown[]): void;\n  info(...parts: unknown[]): void;\n  warn(...parts: unknown[]): void;\n  error(...parts: unknown[]): void;\n}\n```\n\n</details>\n\n<details>\n<summary>overrideNativeConsole</summary>\n\nCalls overrideLogger for native console.\n\n```\noverrideNativeConsole()\n```\n\n</details>\n\n<details>\n<summary>catchProcessErrors</summary>\n\nSubscribes to `unhandledRejection` and `uncaughtException` events of the process to report `error` in such cases.\n\n```ts\ncatchProcessErrors();\n```\n\n</details>\n\n### ServerMonitoringService\n\n<details>\n<summary>initServerMonitoring</summary>\n\nInstantiate ServerMonitoringService with provided params and may also do additional work,\ndepending on provided variables.\n\n```\ninitServerMonitoring = (\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  monitoringOptions?: MonitoringOptions,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig\n): ServerMonitoringService\n```\n\n```ts\ninterface MonitoringOptions {\n  shouldOverrideNativeConsole?: boolean;\n  shouldCatchProcessErrors?: boolean;\n  globalMonitoringInstanceName?: string;\n}\n```\n\n- `RemoteMonitoringServiceParams` and `RemoteMonitoringServiceConfig` are same as in `constructor` api\n- `shouldOverrideNativeConsole` - if `true`, will call `serverMonitoringService.overrideNativeConsole()` under the hood\n- `shouldCatchProcessErrors` - if `true`, will call `serverMonitoringService.catchProcessErrors()` under the hood\n- `globalMonitoringInstanceName` - if provided with non-empty string will put instantiated `serverMonitoringService` into global scope under provided name.\n\n</details>\n","readmeFilename":"README.md","gitHead":"b53e1221acafb4258cfa22a41c2fe68bc224ca96","description":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)","_id":"@kilohealth/web-app-monitoring@2.0.1-alpha.1","_nodeVersion":"18.17.0","_npmVersion":"9.7.2","dist":{"integrity":"sha512-jfGobbJvCVxbLB4xKpE/Ps8rpu2IgSKGk+SyV4LS6E9IAcxF1adpHon32kaLdp/6z9Ecx1Zc/0s5g0MrDxBymw==","shasum":"350da82a28258a32a45d53ed41436c8100fcd410","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-2.0.1-alpha.1.tgz","fileCount":40,"unpackedSize":54985,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCMJa/BzK5WjgXtCEQSoT9CzLaUkY9yR8HDEIZAPRcuwgIhAN96QAeBM8wecG4P2oCnmhxRvuzieMdMcPCBu3GYfJzd"}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_2.0.1-alpha.1_1692526683123_0.679656360218786"},"_hasShrinkwrap":false},"2.0.1":{"name":"@kilohealth/web-app-monitoring","version":"2.0.1","license":"MIT","author":{"name":"Kilo Health"},"sideEffects":false,"exports":{"./dist/server/initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./dist/server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./dist/browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","githooks:commit-msg":"commitlint --edit","githooks:pre-commit":"lint-staged","prepare":"husky install","semantic-release":"semantic-release","test":"jest"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","dd-trace":"^4.0.0","deepmerge":"^4.3.1","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","@types/lodash":"^4.14.195","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jest-environment-jsdom":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"gitHead":"6cbec78347640f93474f3ef2af5a78a25372e524","description":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)","_id":"@kilohealth/web-app-monitoring@2.0.1","_nodeVersion":"18.17.0","_npmVersion":"9.7.2","dist":{"integrity":"sha512-aPtFHEjKIJpPp0hsHXl2lpwb6PwZZLInvocyl03crYadEv02991hRvkJyQTGw+Am5Q8nCPvFsSPj3lA2yPflCg==","shasum":"ee72bd8d2eaef3ab2576e5360a38ea06300017d1","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-2.0.1.tgz","fileCount":40,"unpackedSize":54977,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAFrGpjNv21EuwIAnfTXlwT/6cpKpkN8GwOa0ip9gpeqAiBK3YAruWFkS7xNWSLpaSHFoZHLy/rvY70pG1EXSGwDXw=="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_2.0.1_1692598798286_0.34651494515102277"},"_hasShrinkwrap":false},"2.0.2-alpha.1":{"name":"@kilohealth/web-app-monitoring","version":"2.0.2-alpha.1","license":"MIT","author":{"name":"Kilo Health"},"sideEffects":false,"exports":{"./dist/server/initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./dist/server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./dist/browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","githooks:commit-msg":"commitlint --edit","githooks:pre-commit":"lint-staged","prepare":"husky install","semantic-release":"semantic-release","test":"jest"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","dd-trace":"^4.0.0","deepmerge":"^4.3.1","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","@types/lodash":"^4.14.195","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jest-environment-jsdom":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"readme":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)\n\n# @kilohealth/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\n## The Idea\n\nPackage was created to abstract away underlying level of monitoring,\nto make it easy to setup monitoring for any env as well as to make it easy to migrate from DataDog to other monitoring solution.\n\nThe package consists of 3 parts:\n\n- Browser / Client monitoring (browser logs)\n- CLI (needed to upload sourcemaps for browser monitoring)\n- Server monitoring (server logs, APM, tracing)\n\n## Getting Started\n\n> **Note:** If you are migrating from direct datadog integration - don’t forget to remove `@datadog/...` dependencies. Those are now dependencies of `@kilohealth/web-app-monitoring`.\n>\n> ```\n> npm uninstall @datadog/...\n> ```\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n### Setup environment variables\n\n| Variable                           | Description                                                                                                                                                                                                                                              | Upload source maps | Server (APM, tracing) | Browser / Client |\n| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------: | :-------------------: | :--------------: |\n| `MONITORING_TOOL__API_KEY`         | This key is needed in order to uploaded source maps for browser monitoring, send server side (APM) logs and tracing info. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048). |         ✔️         |          ✔️           |                  |\n| `MONITORING_TOOL__SERVICE_NAME`    | The service name, for example: `timely-hand-web-funnel-app`.                                                                                                                                                                                             |         ✔️         |          ✔️           |        ✔️        |\n| `MONITORING_TOOL__SERVICE_VERSION` | The service version, for example: `$CI_COMMIT_SHA`.                                                                                                                                                                                                      |         ✔️         |          ✔️           |        ✔️        |\n| `MONITORING_TOOL__SERVICE_ENV`     | The service environment, for example: `$CI_ENVIRONMENT_NAME`.                                                                                                                                                                                            |         ✔️         |          ✔️           |        ✔️        |\n| `MONITORING_TOOL__CLIENT_TOKEN`    | This token is needed in order to send browser monitoring logs. You can create or find client token [here](https://app.datadoghq.com/organization-settings/client-tokens).                                                                                |         ️          |                       |        ✔️        |\n\n> **Note:** Depending on the framework you are using, in order to expose environment variables to the client you may need to prefix the environment variables as mentioned below:\n>\n> - For Next.js, add the prefix `NEXT_PUBLIC_` to each variable. Refer to the [documentation](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#bundling-environment-variables-for-the-browser) for more details.\n> - For Gatsby.js, add the prefix `GATSBY_` to each variable. Refer to the [documentation](https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/#accessing-environment-variables-in-the-browser) for more details.\n> - For Vite.js, add the prefix `VITE_` to each variable. Refer to the [documentation](https://vitejs.dev/guide/env-and-mode.html) for more details.\n\n> **Tip:** By following Single Source of Truth principle you can reexport variables, needed for the client, in the build stage (Next.js example):\n>\n> ```\n> NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME\n> NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION=$MONITORING_TOOL__SERVICE_VERSION\n> NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV=$MONITORING_TOOL__SERVICE_ENV\n> ```\n\n### Setup browser monitoring\n\n#### Generate hidden source maps\n\nIn order to upload source maps into the monitoring service we need to include those source map files into our build.\nThis can be done by slightly altering the build phase bundler configuration of our app:\n\n<details>\n<summary>Next.js (next.config.js)</summary>\n\n```js\nmodule.exports = {\n  webpack: (config, context) => {\n    const isClient = !context.isServer;\n    const isProd = !context.dev;\n    const isSourcemapsUploadEnabled = Boolean(\n      process.env.MONITORING_TOOL__API_KEY,\n    );\n\n    // Generate source maps only for the client side production build\n    if (isClient && isProd && isSourcemapsUploadEnabled) {\n      return {\n        ...config,\n        // No reference. No source maps exposure to the client (browser).\n        // Hidden source maps generation only for error reporting purposes.\n        devtool: 'hidden-source-map',\n      };\n    }\n\n    return config;\n  },\n};\n```\n\nRefer to the [documentation](https://webpack.js.org/configuration/devtool/) for more details.\n\n</details>\n\n<details>\n<summary>Gatsby.js (gatsby-node.js)</summary>\n\n```js\nmodule.exports = {\n  onCreateWebpackConfig: ({ stage, actions }) => {\n    const isSourcemapsUploadEnabled = Boolean(\n      process.env.MONITORING_TOOL__API_KEY,\n    );\n    // build-javascript is prod build phase\n    if (stage === 'build-javascript' && isSourcemapsUploadEnabled) {\n      actions.setWebpackConfig({\n        // No reference. No source maps exposure to the client (browser).\n        // Hidden source maps generation only for error reporting purposes.\n        devtool: 'hidden-source-map',\n      });\n    }\n  },\n};\n```\n\nRefer to the [documentation](https://webpack.js.org/configuration/devtool/) for more details.\n\n</details>\n\n<details>\n<summary>Vite.js (vite.config.js)</summary>\n\n```js\nexport default defineConfig({\n  build: {\n    // No reference. No source maps exposure to the client (browser).\n    // Hidden source maps generation only for error reporting purposes.\n    sourcemap: process.env.MONITORING_TOOL__API_KEY ? 'hidden' : false,\n  },\n});\n```\n\nRefer to the [documentation](https://vitejs.dev/config/build-options.html#build-sourcemap) for more details.\n\n</details>\n\n> **Note:** We are using `hidden source maps` only for error reporting purposes.\n> That means our source maps are not exposed to the client\n> and there are no references to those source maps in our source code.\n\n#### Upload generated source maps\n\nIn order to upload generated source maps into the monitoring service, you should use `web-app-monitoring__upload-sourcemaps` bin, provided by `@kilohealth/web-app-monitoring` package.\nTo run the script you need to provide arguments:\n\n| Argument               | Description                                                                                                                                                                                                                                                                                                                                                                                                                                             |   Vite   |                              Next                               |   Gatsby   |\n| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------: | :-------------------------------------------------------------: | :--------: |\n| `--buildDir` or `-d`   | This should be RELATIVE path to your build directory. For example `./dist` or `./build`.                                                                                                                                                                                                                                                                                                                                                                | `./dist` |                     `./.next/static/chunks`                     | `./public` |\n| `--publicPath` or `-p` | This is RELATIVE path, part of URL between domain (which can be different for different environments) and path to file itself. In other words - base path for all the assets within your application.<br/>You can think of this as kind of relative [Public Path](https://webpack.js.org/guides/public-path/). For example it can be `/` or `/static`. In other words this is common relative prefix for all your static files or `/` if there is none. |   `/`    | `/_next/static/chunks` (!!! `_` instead of `.` in file system)️ |    `/`     |\n\nScript example for Next.js:\n\n```\n\"scripts\": {\n  \"upload:sourcemaps\": \"web-app-monitoring__upload-sourcemaps --buildDir ./.next/static/chunks --publicPath /_next/static/chunks\",\n  ...\n},\n```\n\nAnd then your CI should run `upload:sourcemaps` script for the build that includes generated source maps.\n\n### Browser Monitoring Usage\n\n> **Important note:** There is no single entry point for package. You can't do something like `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring';`\n> Reason for that is to avoid bundling server-code into client bundle and vice versa. This structure will ensure effective tree shaking during build time.\n\n> In case your bundler supports package.json `exports` field - you can also omit `dist` in path folder `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/browser';`\n\n```ts\nimport { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/dist/browser';\n\nexport const monitoring = new BrowserMonitoringService({\n  authToken: NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN,\n  serviceName: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV,\n});\n```\n\nAs you can see we are using here all our exposed variables. If any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n**OPTIONAL: If you are using React you may benefit from utilizing [Error Boundaries](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary):**\n\n```tsx\nimport React, { Component, PropsWithChildren } from 'react';\nimport { monitoring } from '../services/monitoring';\n\ninterface ErrorBoundaryProps {}\ninterface ErrorBoundaryState {\n  hasError: boolean;\n}\n\nexport class ErrorBoundary extends Component<\n  PropsWithChildren<ErrorBoundaryProps>,\n  ErrorBoundaryState\n> {\n  static getDerivedStateFromError(_: Error): ErrorBoundaryState {\n    return { hasError: true };\n  }\n\n  state: ErrorBoundaryState = {\n    hasError: false,\n  };\n\n  componentDidCatch(error: Error) {\n    monitoring.reportError(error);\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return <h1>Sorry... There was an error</h1>;\n    }\n\n    return this.props.children;\n  }\n}\n```\n\n### Setup Server Monitoring (Next.js)\n\n<details>\n<summary>Approach with facade</summary>\n\n`initServerMonitoring` is a facade over `ServerMonitoringService`.\nYou can instantiate and use that service directly.\nThis function basically does one thing - instantiate it and can do 3 more additional things:\n\n- call `overrideNativeConsole` - method of the service to override native console, to log to datadog instead.\n- cal `catchProcessErrors` - method of the service to subscribe to native errors, to log them to datadog.\n- put service itself into global scope under defined name, so serverside code can use it.\n\nYou may wonder why we instantiate service here and not in server-side code.\nThe reason for that is if we override the native console and catch native errors - we would like to set up this as soon as possible.\nIf you don’t care too much about the very first seconds of next server - you can use alternative simpler server side logging solution.\n\nExample for Next.js:\n\n- update next.config.ts to include into start script of production server code\n  `next.config.ts:`\n\n```js\nconst {\n  initServerMonitoring,\n} = require('@kilohealth/web-app-monitoring/dist/server');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    const remoteMonitoringServiceParams = {\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    };\n    const config = {\n      shouldOverrideNativeConsole: true,\n      shouldCatchProcessErrors: true,\n      globalMonitoringInstanceName: 'kiloServerMonitoring',\n    };\n    initServerMonitoring(remoteMonitoringServiceParams, config);\n  }\n};\n```\n\n- update `custom.d.ts` file to declare that global scope now have monitoring service as a prop\n  In order to use ServerMonitoringService instance in other parts of code via global we need to let TS know\n  that we added new property to global object.\n  In Next.js you can just create or add next code into `custom.d.ts` file in root of the project.\n  Be aware that var name matches string that you provided in code above (kiloServerMonitoring in this case).\n  `custom.d.ts:`\n\n```ts\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\ndeclare global {\n  // eslint-disable-next-line no-var\n  var kiloServerMonitoring: ServerMonitoringService;\n}\n```\n\n- use it in code\n\n```ts\nexport const getHomeServerSideProps = async context => {\n  global.kiloServerMonitoring.info('getHomeServerSideProps called');\n};\n```\n\n</details>\n\n<details>\n<summary>Approach with direct instantiation</summary>\n\nIf you don’t care too much about catching native errors or native logs\nin the early stages of your server app -\nyou can avoid sharing logger via global scope and instead initialize it inside of app.\n\n```ts\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\nexport const monitoring = new ServerMonitoringService({\n  authToken: MONITORING_TOOL__API_KEY,\n  serviceName: MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: MONITORING_TOOL__SERVICE_ENV,\n});\n```\n\nAs you can see we are using here all our env variables.\nIf any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n</details>\n\n#### Init Tracing\n\nWe need to connect tracing as soon as possible during code, so it can be injected into all base modules for APM monitoring.\nTracing module is available via:\n\n```js\nconst {\n  initTracing,\n} = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\n\ninitTracing({\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n});\n```\n\nExample for Next.js:\n\n```js\nconst { PHASE_PRODUCTION_SERVER } = require('next/constants');\nconst {\n  initTracing,\n} = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    initTracing({\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    });\n  }\n};\n```\n\n> **Note:** In newer versions of Next.js there is experimental feature called\n> [instrumentationHook](https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation)\n> We can opt out from using undocumented `PHASE_PRODUCTION_SERVER` to use `instrumentationHook` for tracing init.\n> There is also possibility to use `NODE_OPTIONS='-r ./prestart-script.js ' next start` instead.\n> But there is an issue with `pino-datadog-transport`, which for performance reason spawns separate thread for log sending to data-dog and it this option seems to be passed to that process as well which triggers an infinite loop of require and initialization.\n\n## API\n\n### MonitoringService (both BrowserMonitoringService and ServerMonitoringService have these methods)\n\n<details>\n<summary>debug, info, warn</summary>\n\n```\ndebug(message: string, context?: object)\ninfo(message: string, context?: object)\nwarn(message: string, context?: object)\n```\n\n- `message` - any message to be logged\n- `context` - object with all needed and related to the log entrance data\n</details>\n\n<details>\n<summary>error</summary>\n\n```\nerror(message: string, context?: object, error?: Error)\n```\n\nSame as above, but you can also optionally pass error instance as third parameter\n\n</details>\n\n<details>\n<summary>reportError</summary>\n\n```\nreportError(error: Error, context?: object)\n```\n\nShortcut for `service.error()`, which uses `error.message` field as message param for `error` method.\n\n</details>\n\n### BrowserMonitoringService\n\n<details>\n<summary>constructor</summary>\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```ts\ninterface RemoteMonitoringServiceParams {\n  serviceName?: string;\n  serviceVersion?: string;\n  serviceEnv?: string;\n  authToken?: string;\n}\n```\n\n- `RemoteMonitoringServiceConfig` - datadog params passed to init function. More info in [docs](https://docs.datadoghq.com/logs/log_collection/javascript/#initialization-parameters)\n- `serviceName` - name of the service\n- `serviceVersion` - version of the service\n- `serviceEnv` - environment where service is deployed\n- `authToken` - client token\n</details>\n\n### ServerMonitoringService\n\n<details>\n<summary>constructor</summary>\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```ts\ninterface RemoteMonitoringServiceConfig {\n  transportOptions?: Partial<TransportBaseOptions>;\n  loggerOptions?: Partial<LoggerOptions>;\n}\n```\n\n- `transportOptions` - [pino-datadog-transport options](https://github.com/theogravity/pino-datadog-transport#configuration-options)\n- `loggerOptions` - [pino logger options](https://getpino.io/#/docs/api?id=options-object)\n</details>\n\n<details>\n<summary>overrideLogger</summary>\n\nOverrides logger passed as argument with monitoring logger.\nAll methods of this logger will be overridden with corresponding methods of server monitoring.\n\n```\noverrideLogger(unknownLogger: UnknownLogger)\n```\n\n```ts\ninterface UnknownLogger {\n  log?(...parts: unknown[]): void;\n  debug?(...parts: unknown[]): void;\n  info(...parts: unknown[]): void;\n  warn(...parts: unknown[]): void;\n  error(...parts: unknown[]): void;\n}\n```\n\n</details>\n\n<details>\n<summary>overrideNativeConsole</summary>\n\nCalls overrideLogger for native console.\n\n```\noverrideNativeConsole()\n```\n\n</details>\n\n<details>\n<summary>catchProcessErrors</summary>\n\nSubscribes to `unhandledRejection` and `uncaughtException` events of the process to report `error` in such cases.\n\n```ts\ncatchProcessErrors();\n```\n\n</details>\n\n### ServerMonitoringService\n\n<details>\n<summary>initServerMonitoring</summary>\n\nInstantiate ServerMonitoringService with provided params and may also do additional work,\ndepending on provided variables.\n\n```\ninitServerMonitoring = (\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  monitoringOptions?: MonitoringOptions,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig\n): ServerMonitoringService\n```\n\n```ts\ninterface MonitoringOptions {\n  shouldOverrideNativeConsole?: boolean;\n  shouldCatchProcessErrors?: boolean;\n  globalMonitoringInstanceName?: string;\n}\n```\n\n- `RemoteMonitoringServiceParams` and `RemoteMonitoringServiceConfig` are same as in `constructor` api\n- `shouldOverrideNativeConsole` - if `true`, will call `serverMonitoringService.overrideNativeConsole()` under the hood\n- `shouldCatchProcessErrors` - if `true`, will call `serverMonitoringService.catchProcessErrors()` under the hood\n- `globalMonitoringInstanceName` - if provided with non-empty string will put instantiated `serverMonitoringService` into global scope under provided name.\n\n</details>\n","readmeFilename":"README.md","gitHead":"bb7ff7dd06c5172020c0ec1274efee5f9db973dd","description":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)","_id":"@kilohealth/web-app-monitoring@2.0.2-alpha.1","_nodeVersion":"18.17.1","_npmVersion":"9.7.2","dist":{"integrity":"sha512-EqmPo0OrIvxpgioxOKIJZT4mrccDqIgLYt2bEXaS9Ms+2y8aUepWNXyjIVDMwR4iWMdrB6vLHnXF/aADZ2ZOCg==","shasum":"2a6d2a894ffee1bf22a117033bc12510158fcb4e","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-2.0.2-alpha.1.tgz","fileCount":40,"unpackedSize":55020,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF3SoTrOzzlNygPSn6ypuAYAZo3AaPSZeZ0Dmpna4pUzAiBhKydekoNiUz4ubfbBqgYD0zi+SCvTSkTZo35QminshA=="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_2.0.2-alpha.1_1693575975652_0.41192350398877475"},"_hasShrinkwrap":false},"2.0.2":{"name":"@kilohealth/web-app-monitoring","version":"2.0.2","license":"MIT","author":{"name":"Kilo Health"},"sideEffects":false,"exports":{"./dist/server/initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./dist/server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./dist/browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","githooks:commit-msg":"commitlint --edit","githooks:pre-commit":"lint-staged","prepare":"husky install","semantic-release":"semantic-release","test":"jest"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.42.2","dd-trace":"^4.0.0","deepmerge":"^4.3.1","pino":"^8.14.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.6.3","@commitlint/config-conventional":"^17.6.3","@kilohealth/eslint-config-node":"^1.6.1","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^9.0.2","@semantic-release/git":"^10.0.1","@semantic-release/github":"^8.0.7","@semantic-release/npm":"^10.0.3","@semantic-release/release-notes-generator":"^11.0.1","@types/jest":"^29.5.1","@types/lodash":"^4.14.195","eslint":"^8.41.0","husky":"^8.0.3","jest":"^29.5.0","jest-environment-jsdom":"^29.5.0","jsdom":"^22.0.0","lint-staged":"^13.2.2","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.0.2","sort-package-json":"^2.4.1","ts-jest":"^29.1.0","typescript":"^5.0.4"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"gitHead":"04fde4104dd96f54bbfbe2dcda075292d0fb8a5b","description":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)","_id":"@kilohealth/web-app-monitoring@2.0.2","_nodeVersion":"18.17.1","_npmVersion":"9.7.2","dist":{"integrity":"sha512-yCvOppXBcEk21pNIzRegXmZ8Dg4uv6sjOb4rxyutXTKg7qE+nSXBfUvBzTYAr+M4y8HKRw7l9n6Oc3bd/y+2Fw==","shasum":"bae88cc4f55c97e4d2c079b13296d48c55bf9e1f","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-2.0.2.tgz","fileCount":40,"unpackedSize":55012,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCPdzz5j/b4eZKp7wwVZVlrp0G5QiHnh2vI9TvtgTaA5QIhAI5u0B1/T67RciN+zN9fAeA2taiKWVFVGtg/nReoUX2g"}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_2.0.2_1693815024524_0.5659520718820941"},"_hasShrinkwrap":false},"2.1.0-alpha.1":{"name":"@kilohealth/web-app-monitoring","version":"2.1.0-alpha.1","license":"MIT","author":{"name":"Kilo Health"},"sideEffects":false,"exports":{"./dist/server/initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./dist/server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./dist/browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","githooks:commit-msg":"commitlint --edit","githooks:pre-commit":"lint-staged","prepare":"husky install","semantic-release":"semantic-release","test":"jest"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.48.1","dd-trace":"^4.14.0","deepmerge":"^4.3.1","pino":"^8.15.0","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.7.1","@commitlint/config-conventional":"^17.7.0","@kilohealth/eslint-config-node":"^1.7.0","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/git":"^10.0.1","@types/jest":"^29.5.4","@types/lodash":"^4.14.197","eslint":"^8.48.0","husky":"^8.0.3","jest":"^29.6.4","jest-environment-jsdom":"^29.6.4","jsdom":"^22.1.0","lint-staged":"^14.0.1","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.1.1","sort-package-json":"^2.5.1","ts-jest":"^29.1.1","typescript":"^5.2.2"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_id":"@kilohealth/web-app-monitoring@2.1.0-alpha.1","readme":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)\n\n# @kilohealth/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\n## The Idea\n\nPackage was created to abstract away underlying level of monitoring,\nto make it easy to setup monitoring for any env as well as to make it easy to migrate from DataDog to other monitoring solution.\n\nThe package consists of 3 parts:\n\n- Browser / Client monitoring (browser logs)\n- CLI (needed to upload sourcemaps for browser monitoring)\n- Server monitoring (server logs, APM, tracing)\n\n## Getting Started\n\n> **Note:** If you are migrating from direct datadog integration - don’t forget to remove `@datadog/...` dependencies. Those are now dependencies of `@kilohealth/web-app-monitoring`.\n>\n> ```\n> npm uninstall @datadog/...\n> ```\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n### Setup environment variables\n\n| Variable                           | Description                                                                                                                                                                                                                                              | Upload source maps | Server (APM, tracing) | Browser / Client |\n| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------: | :-------------------: | :--------------: |\n| `MONITORING_TOOL__API_KEY`         | This key is needed in order to uploaded source maps for browser monitoring, send server side (APM) logs and tracing info. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048). |         ✔️         |          ✔️           |                  |\n| `MONITORING_TOOL__SERVICE_NAME`    | The service name, for example: `timely-hand-web-funnel-app`.                                                                                                                                                                                             |         ✔️         |          ✔️           |        ✔️        |\n| `MONITORING_TOOL__SERVICE_VERSION` | The service version, for example: `$CI_COMMIT_SHA`.                                                                                                                                                                                                      |         ✔️         |          ✔️           |        ✔️        |\n| `MONITORING_TOOL__SERVICE_ENV`     | The service environment, for example: `$CI_ENVIRONMENT_NAME`.                                                                                                                                                                                            |         ✔️         |          ✔️           |        ✔️        |\n| `MONITORING_TOOL__CLIENT_TOKEN`    | This token is needed in order to send browser monitoring logs. You can create or find client token [here](https://app.datadoghq.com/organization-settings/client-tokens).                                                                                |         ️          |                       |        ✔️        |\n\n> **Note:** Depending on the framework you are using, in order to expose environment variables to the client you may need to prefix the environment variables as mentioned below:\n>\n> - For Next.js, add the prefix `NEXT_PUBLIC_` to each variable. Refer to the [documentation](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#bundling-environment-variables-for-the-browser) for more details.\n> - For Gatsby.js, add the prefix `GATSBY_` to each variable. Refer to the [documentation](https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/#accessing-environment-variables-in-the-browser) for more details.\n> - For Vite.js, add the prefix `VITE_` to each variable. Refer to the [documentation](https://vitejs.dev/guide/env-and-mode.html) for more details.\n\n> **Tip:** By following Single Source of Truth principle you can reexport variables, needed for the client, in the build stage (Next.js example):\n>\n> ```\n> NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME\n> NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION=$MONITORING_TOOL__SERVICE_VERSION\n> NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV=$MONITORING_TOOL__SERVICE_ENV\n> ```\n\n### Setup browser monitoring\n\n#### Generate hidden source maps\n\nIn order to upload source maps into the monitoring service we need to include those source map files into our build.\nThis can be done by slightly altering the build phase bundler configuration of our app:\n\n<details>\n<summary>Next.js (next.config.js)</summary>\n\n```js\nmodule.exports = {\n  webpack: (config, context) => {\n    const isClient = !context.isServer;\n    const isProd = !context.dev;\n    const isSourcemapsUploadEnabled = Boolean(\n      process.env.MONITORING_TOOL__API_KEY,\n    );\n\n    // Generate source maps only for the client side production build\n    if (isClient && isProd && isSourcemapsUploadEnabled) {\n      return {\n        ...config,\n        // No reference. No source maps exposure to the client (browser).\n        // Hidden source maps generation only for error reporting purposes.\n        devtool: 'hidden-source-map',\n      };\n    }\n\n    return config;\n  },\n};\n```\n\nRefer to the [documentation](https://webpack.js.org/configuration/devtool/) for more details.\n\n</details>\n\n<details>\n<summary>Gatsby.js (gatsby-node.js)</summary>\n\n```js\nmodule.exports = {\n  onCreateWebpackConfig: ({ stage, actions }) => {\n    const isSourcemapsUploadEnabled = Boolean(\n      process.env.MONITORING_TOOL__API_KEY,\n    );\n    // build-javascript is prod build phase\n    if (stage === 'build-javascript' && isSourcemapsUploadEnabled) {\n      actions.setWebpackConfig({\n        // No reference. No source maps exposure to the client (browser).\n        // Hidden source maps generation only for error reporting purposes.\n        devtool: 'hidden-source-map',\n      });\n    }\n  },\n};\n```\n\nRefer to the [documentation](https://webpack.js.org/configuration/devtool/) for more details.\n\n</details>\n\n<details>\n<summary>Vite.js (vite.config.js)</summary>\n\n```js\nexport default defineConfig({\n  build: {\n    // No reference. No source maps exposure to the client (browser).\n    // Hidden source maps generation only for error reporting purposes.\n    sourcemap: process.env.MONITORING_TOOL__API_KEY ? 'hidden' : false,\n  },\n});\n```\n\nRefer to the [documentation](https://vitejs.dev/config/build-options.html#build-sourcemap) for more details.\n\n</details>\n\n> **Note:** We are using `hidden source maps` only for error reporting purposes.\n> That means our source maps are not exposed to the client\n> and there are no references to those source maps in our source code.\n\n#### Upload generated source maps\n\nIn order to upload generated source maps into the monitoring service, you should use `web-app-monitoring__upload-sourcemaps` bin, provided by `@kilohealth/web-app-monitoring` package.\nTo run the script you need to provide arguments:\n\n| Argument               | Description                                                                                                                                                                                                                                                                                                                                                                                                                                             |   Vite   |                              Next                               |   Gatsby   |\n| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------: | :-------------------------------------------------------------: | :--------: |\n| `--buildDir` or `-d`   | This should be RELATIVE path to your build directory. For example `./dist` or `./build`.                                                                                                                                                                                                                                                                                                                                                                | `./dist` |                     `./.next/static/chunks`                     | `./public` |\n| `--publicPath` or `-p` | This is RELATIVE path, part of URL between domain (which can be different for different environments) and path to file itself. In other words - base path for all the assets within your application.<br/>You can think of this as kind of relative [Public Path](https://webpack.js.org/guides/public-path/). For example it can be `/` or `/static`. In other words this is common relative prefix for all your static files or `/` if there is none. |   `/`    | `/_next/static/chunks` (!!! `_` instead of `.` in file system)️ |    `/`     |\n\nScript example for Next.js:\n\n```\n\"scripts\": {\n  \"upload:sourcemaps\": \"web-app-monitoring__upload-sourcemaps --buildDir ./.next/static/chunks --publicPath /_next/static/chunks\",\n  ...\n},\n```\n\nAnd then your CI should run `upload:sourcemaps` script for the build that includes generated source maps.\n\n### Browser Monitoring Usage\n\n> **Important note:** There is no single entry point for package. You can't do something like `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring';`\n> Reason for that is to avoid bundling server-code into client bundle and vice versa. This structure will ensure effective tree shaking during build time.\n\n> In case your bundler supports package.json `exports` field - you can also omit `dist` in path folder `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/browser';`\n\n```ts\nimport { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/dist/browser';\n\nexport const monitoring = new BrowserMonitoringService({\n  authToken: NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN,\n  serviceName: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV,\n});\n```\n\nAs you can see we are using here all our exposed variables. If any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n**OPTIONAL: If you are using React you may benefit from utilizing [Error Boundaries](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary):**\n\n```tsx\nimport React, { Component, PropsWithChildren } from 'react';\nimport { monitoring } from '../services/monitoring';\n\ninterface ErrorBoundaryProps {}\ninterface ErrorBoundaryState {\n  hasError: boolean;\n}\n\nexport class ErrorBoundary extends Component<\n  PropsWithChildren<ErrorBoundaryProps>,\n  ErrorBoundaryState\n> {\n  static getDerivedStateFromError(_: Error): ErrorBoundaryState {\n    return { hasError: true };\n  }\n\n  state: ErrorBoundaryState = {\n    hasError: false,\n  };\n\n  componentDidCatch(error: Error) {\n    monitoring.reportError(error);\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return <h1>Sorry... There was an error</h1>;\n    }\n\n    return this.props.children;\n  }\n}\n```\n\n### Setup Server Monitoring (Next.js)\n\n<details>\n<summary>Approach with facade</summary>\n\n`initServerMonitoring` is a facade over `ServerMonitoringService`.\nYou can instantiate and use that service directly.\nThis function basically does one thing - instantiate it and can do 3 more additional things:\n\n- call `overrideNativeConsole` - method of the service to override native console, to log to datadog instead.\n- cal `catchProcessErrors` - method of the service to subscribe to native errors, to log them to datadog.\n- put service itself into global scope under defined name, so serverside code can use it.\n\nYou may wonder why we instantiate service here and not in server-side code.\nThe reason for that is if we override the native console and catch native errors - we would like to set up this as soon as possible.\nIf you don’t care too much about the very first seconds of next server - you can use alternative simpler server side logging solution.\n\nExample for Next.js:\n\n- update next.config.ts to include into start script of production server code\n  `next.config.ts:`\n\n```js\nconst {\n  initServerMonitoring,\n} = require('@kilohealth/web-app-monitoring/dist/server');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    const remoteMonitoringServiceParams = {\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    };\n    const config = {\n      shouldOverrideNativeConsole: true,\n      shouldCatchProcessErrors: true,\n      globalMonitoringInstanceName: 'kiloServerMonitoring',\n    };\n    initServerMonitoring(remoteMonitoringServiceParams, config);\n  }\n};\n```\n\n- update `custom.d.ts` file to declare that global scope now have monitoring service as a prop\n  In order to use ServerMonitoringService instance in other parts of code via global we need to let TS know\n  that we added new property to global object.\n  In Next.js you can just create or add next code into `custom.d.ts` file in root of the project.\n  Be aware that var name matches string that you provided in code above (kiloServerMonitoring in this case).\n  `custom.d.ts:`\n\n```ts\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\ndeclare global {\n  // eslint-disable-next-line no-var\n  var kiloServerMonitoring: ServerMonitoringService;\n}\n```\n\n- use it in code\n\n```ts\nexport const getHomeServerSideProps = async context => {\n  global.kiloServerMonitoring.info('getHomeServerSideProps called');\n};\n```\n\n</details>\n\n<details>\n<summary>Approach with direct instantiation</summary>\n\nIf you don’t care too much about catching native errors or native logs\nin the early stages of your server app -\nyou can avoid sharing logger via global scope and instead initialize it inside of app.\n\n```ts\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\nexport const monitoring = new ServerMonitoringService({\n  authToken: MONITORING_TOOL__API_KEY,\n  serviceName: MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: MONITORING_TOOL__SERVICE_ENV,\n});\n```\n\nAs you can see we are using here all our env variables.\nIf any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n</details>\n\n#### Init Tracing\n\nWe need to connect tracing as soon as possible during code, so it can be injected into all base modules for APM monitoring.\nTracing module is available via:\n\n```js\nconst {\n  initTracing,\n} = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\n\ninitTracing({\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n});\n```\n\nExample for Next.js:\n\n```js\nconst { PHASE_PRODUCTION_SERVER } = require('next/constants');\nconst {\n  initTracing,\n} = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    initTracing({\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    });\n  }\n};\n```\n\n> **Note:** In newer versions of Next.js there is experimental feature called\n> [instrumentationHook](https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation)\n> We can opt out from using undocumented `PHASE_PRODUCTION_SERVER` to use `instrumentationHook` for tracing init.\n> There is also possibility to use `NODE_OPTIONS='-r ./prestart-script.js ' next start` instead.\n> But there is an issue with `pino-datadog-transport`, which for performance reason spawns separate thread for log sending to data-dog and it this option seems to be passed to that process as well which triggers an infinite loop of require and initialization.\n\n## API\n\n### MonitoringService (both BrowserMonitoringService and ServerMonitoringService have these methods)\n\n<details>\n<summary>debug, info, warn</summary>\n\n```\ndebug(message: string, context?: object)\ninfo(message: string, context?: object)\nwarn(message: string, context?: object)\n```\n\n- `message` - any message to be logged\n- `context` - object with all needed and related to the log entrance data\n</details>\n\n<details>\n<summary>error</summary>\n\n```\nerror(message: string, context?: object, error?: Error)\n```\n\nSame as above, but you can also optionally pass error instance as third parameter\n\n</details>\n\n<details>\n<summary>reportError</summary>\n\n```\nreportError(error: Error, context?: object)\n```\n\nShortcut for `service.error()`, which uses `error.message` field as message param for `error` method.\n\n</details>\n\n### BrowserMonitoringService\n\n<details>\n<summary>constructor</summary>\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```ts\ninterface RemoteMonitoringServiceParams {\n  serviceName?: string;\n  serviceVersion?: string;\n  serviceEnv?: string;\n  authToken?: string;\n}\n```\n\n- `RemoteMonitoringServiceConfig` - datadog params passed to init function. More info in [docs](https://docs.datadoghq.com/logs/log_collection/javascript/#initialization-parameters)\n- `serviceName` - name of the service\n- `serviceVersion` - version of the service\n- `serviceEnv` - environment where service is deployed\n- `authToken` - client token\n</details>\n\n### ServerMonitoringService\n\n<details>\n<summary>constructor</summary>\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```ts\ninterface RemoteMonitoringServiceConfig {\n  transportOptions?: Partial<TransportBaseOptions>;\n  loggerOptions?: Partial<LoggerOptions>;\n}\n```\n\n- `transportOptions` - [pino-datadog-transport options](https://github.com/theogravity/pino-datadog-transport#configuration-options)\n- `loggerOptions` - [pino logger options](https://getpino.io/#/docs/api?id=options-object)\n</details>\n\n<details>\n<summary>overrideLogger</summary>\n\nOverrides logger passed as argument with monitoring logger.\nAll methods of this logger will be overridden with corresponding methods of server monitoring.\n\n```\noverrideLogger(unknownLogger: UnknownLogger)\n```\n\n```ts\ninterface UnknownLogger {\n  log?(...parts: unknown[]): void;\n  debug?(...parts: unknown[]): void;\n  info(...parts: unknown[]): void;\n  warn(...parts: unknown[]): void;\n  error(...parts: unknown[]): void;\n}\n```\n\n</details>\n\n<details>\n<summary>overrideNativeConsole</summary>\n\nCalls overrideLogger for native console.\n\n```\noverrideNativeConsole()\n```\n\n</details>\n\n<details>\n<summary>catchProcessErrors</summary>\n\nSubscribes to `unhandledRejection` and `uncaughtException` events of the process to report `error` in such cases.\n\n```ts\ncatchProcessErrors();\n```\n\n</details>\n\n### ServerMonitoringService\n\n<details>\n<summary>initServerMonitoring</summary>\n\nInstantiate ServerMonitoringService with provided params and may also do additional work,\ndepending on provided variables.\n\n```\ninitServerMonitoring = (\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  monitoringOptions?: MonitoringOptions,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig\n): ServerMonitoringService\n```\n\n```ts\ninterface MonitoringOptions {\n  shouldOverrideNativeConsole?: boolean;\n  shouldCatchProcessErrors?: boolean;\n  globalMonitoringInstanceName?: string;\n}\n```\n\n- `RemoteMonitoringServiceParams` and `RemoteMonitoringServiceConfig` are same as in `constructor` api\n- `shouldOverrideNativeConsole` - if `true`, will call `serverMonitoringService.overrideNativeConsole()` under the hood\n- `shouldCatchProcessErrors` - if `true`, will call `serverMonitoringService.catchProcessErrors()` under the hood\n- `globalMonitoringInstanceName` - if provided with non-empty string will put instantiated `serverMonitoringService` into global scope under provided name.\n\n</details>\n","readmeFilename":"README.md","gitHead":"fbb10d638eea8d52e0afb1f7390f7b0692c40929","description":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)","_nodeVersion":"18.17.1","_npmVersion":"9.8.1","dist":{"integrity":"sha512-SZx6z9sWmZmLUwGGnwW+7OHjKiSMbX38GiHtqn+ZbVQkXCF47+kcd0ghuMx6oQJ2bM7DKsUTYZ6JWykfAEZunw==","shasum":"e463572d48c6b05bf918d12f6fbd26d68f08edaa","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-2.1.0-alpha.1.tgz","fileCount":40,"unpackedSize":54828,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCjFRnjbE+H1ZKi/CL6l+Dg2Yhesu2gHii3CaX31LeVOQIgYshUYlvoo3BcolY80LiumWcG2/LjD0C1b5oe1vOaHnU="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_2.1.0-alpha.1_1693821167748_0.7336491806593879"},"_hasShrinkwrap":false},"2.1.0-alpha.2":{"name":"@kilohealth/web-app-monitoring","version":"2.1.0-alpha.2","license":"MIT","author":{"name":"Kilo Health"},"sideEffects":false,"exports":{"./dist/server/initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./dist/server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./dist/browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","githooks:commit-msg":"commitlint --edit","githooks:pre-commit":"lint-staged","prepare":"husky install","semantic-release":"semantic-release","test":"jest"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.48.1","dd-trace":"^4.14.0","deepmerge":"^4.3.1","pino":"^8.15.0","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.7.1","@commitlint/config-conventional":"^17.7.0","@kilohealth/eslint-config-node":"^1.7.0","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/git":"^10.0.1","@types/jest":"^29.5.4","@types/lodash":"^4.14.197","eslint":"^8.48.0","husky":"^8.0.3","jest":"^29.6.4","jest-environment-jsdom":"^29.6.4","jsdom":"^22.1.0","lint-staged":"^14.0.1","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.1.1","sort-package-json":"^2.5.1","ts-jest":"^29.1.1","tslib":"^2.6.2","typescript":"^5.2.2"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_id":"@kilohealth/web-app-monitoring@2.1.0-alpha.2","readme":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)\n\n# @kilohealth/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\n## The Idea\n\nPackage was created to abstract away underlying level of monitoring,\nto make it easy to setup monitoring for any env as well as to make it easy to migrate from DataDog to other monitoring solution.\n\nThe package consists of 3 parts:\n\n- Browser / Client monitoring (browser logs)\n- CLI (needed to upload sourcemaps for browser monitoring)\n- Server monitoring (server logs, APM, tracing)\n\n## Getting Started\n\n> **Note:** If you are migrating from direct datadog integration - don’t forget to remove `@datadog/...` dependencies. Those are now dependencies of `@kilohealth/web-app-monitoring`.\n>\n> ```\n> npm uninstall @datadog/...\n> ```\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n### Setup environment variables\n\n| Variable                           | Description                                                                                                                                                                                                                                              | Upload source maps | Server (APM, tracing) | Browser / Client |\n| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------: | :-------------------: | :--------------: |\n| `MONITORING_TOOL__API_KEY`         | This key is needed in order to uploaded source maps for browser monitoring, send server side (APM) logs and tracing info. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048). |         ✔️         |          ✔️           |                  |\n| `MONITORING_TOOL__SERVICE_NAME`    | The service name, for example: `timely-hand-web-funnel-app`.                                                                                                                                                                                             |         ✔️         |          ✔️           |        ✔️        |\n| `MONITORING_TOOL__SERVICE_VERSION` | The service version, for example: `$CI_COMMIT_SHA`.                                                                                                                                                                                                      |         ✔️         |          ✔️           |        ✔️        |\n| `MONITORING_TOOL__SERVICE_ENV`     | The service environment, for example: `$CI_ENVIRONMENT_NAME`.                                                                                                                                                                                            |         ✔️         |          ✔️           |        ✔️        |\n| `MONITORING_TOOL__CLIENT_TOKEN`    | This token is needed in order to send browser monitoring logs. You can create or find client token [here](https://app.datadoghq.com/organization-settings/client-tokens).                                                                                |         ️          |                       |        ✔️        |\n\n> **Note:** Depending on the framework you are using, in order to expose environment variables to the client you may need to prefix the environment variables as mentioned below:\n>\n> - For Next.js, add the prefix `NEXT_PUBLIC_` to each variable. Refer to the [documentation](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#bundling-environment-variables-for-the-browser) for more details.\n> - For Gatsby.js, add the prefix `GATSBY_` to each variable. Refer to the [documentation](https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/#accessing-environment-variables-in-the-browser) for more details.\n> - For Vite.js, add the prefix `VITE_` to each variable. Refer to the [documentation](https://vitejs.dev/guide/env-and-mode.html) for more details.\n\n> **Tip:** By following Single Source of Truth principle you can reexport variables, needed for the client, in the build stage (Next.js example):\n>\n> ```\n> NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME\n> NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION=$MONITORING_TOOL__SERVICE_VERSION\n> NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV=$MONITORING_TOOL__SERVICE_ENV\n> ```\n\n### Setup browser monitoring\n\n#### Generate hidden source maps\n\nIn order to upload source maps into the monitoring service we need to include those source map files into our build.\nThis can be done by slightly altering the build phase bundler configuration of our app:\n\n<details>\n<summary>Next.js (next.config.js)</summary>\n\n```js\nmodule.exports = {\n  webpack: (config, context) => {\n    const isClient = !context.isServer;\n    const isProd = !context.dev;\n    const isSourcemapsUploadEnabled = Boolean(\n      process.env.MONITORING_TOOL__API_KEY,\n    );\n\n    // Generate source maps only for the client side production build\n    if (isClient && isProd && isSourcemapsUploadEnabled) {\n      return {\n        ...config,\n        // No reference. No source maps exposure to the client (browser).\n        // Hidden source maps generation only for error reporting purposes.\n        devtool: 'hidden-source-map',\n      };\n    }\n\n    return config;\n  },\n};\n```\n\nRefer to the [documentation](https://webpack.js.org/configuration/devtool/) for more details.\n\n</details>\n\n<details>\n<summary>Gatsby.js (gatsby-node.js)</summary>\n\n```js\nmodule.exports = {\n  onCreateWebpackConfig: ({ stage, actions }) => {\n    const isSourcemapsUploadEnabled = Boolean(\n      process.env.MONITORING_TOOL__API_KEY,\n    );\n    // build-javascript is prod build phase\n    if (stage === 'build-javascript' && isSourcemapsUploadEnabled) {\n      actions.setWebpackConfig({\n        // No reference. No source maps exposure to the client (browser).\n        // Hidden source maps generation only for error reporting purposes.\n        devtool: 'hidden-source-map',\n      });\n    }\n  },\n};\n```\n\nRefer to the [documentation](https://webpack.js.org/configuration/devtool/) for more details.\n\n</details>\n\n<details>\n<summary>Vite.js (vite.config.js)</summary>\n\n```js\nexport default defineConfig({\n  build: {\n    // No reference. No source maps exposure to the client (browser).\n    // Hidden source maps generation only for error reporting purposes.\n    sourcemap: process.env.MONITORING_TOOL__API_KEY ? 'hidden' : false,\n  },\n});\n```\n\nRefer to the [documentation](https://vitejs.dev/config/build-options.html#build-sourcemap) for more details.\n\n</details>\n\n> **Note:** We are using `hidden source maps` only for error reporting purposes.\n> That means our source maps are not exposed to the client\n> and there are no references to those source maps in our source code.\n\n#### Upload generated source maps\n\nIn order to upload generated source maps into the monitoring service, you should use `web-app-monitoring__upload-sourcemaps` bin, provided by `@kilohealth/web-app-monitoring` package.\nTo run the script you need to provide arguments:\n\n| Argument               | Description                                                                                                                                                                                                                                                                                                                                                                                                                                             |   Vite   |                              Next                               |   Gatsby   |\n| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------: | :-------------------------------------------------------------: | :--------: |\n| `--buildDir` or `-d`   | This should be RELATIVE path to your build directory. For example `./dist` or `./build`.                                                                                                                                                                                                                                                                                                                                                                | `./dist` |                     `./.next/static/chunks`                     | `./public` |\n| `--publicPath` or `-p` | This is RELATIVE path, part of URL between domain (which can be different for different environments) and path to file itself. In other words - base path for all the assets within your application.<br/>You can think of this as kind of relative [Public Path](https://webpack.js.org/guides/public-path/). For example it can be `/` or `/static`. In other words this is common relative prefix for all your static files or `/` if there is none. |   `/`    | `/_next/static/chunks` (!!! `_` instead of `.` in file system)️ |    `/`     |\n\nScript example for Next.js:\n\n```\n\"scripts\": {\n  \"upload:sourcemaps\": \"web-app-monitoring__upload-sourcemaps --buildDir ./.next/static/chunks --publicPath /_next/static/chunks\",\n  ...\n},\n```\n\nAnd then your CI should run `upload:sourcemaps` script for the build that includes generated source maps.\n\n### Browser Monitoring Usage\n\n> **Important note:** There is no single entry point for package. You can't do something like `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring';`\n> Reason for that is to avoid bundling server-code into client bundle and vice versa. This structure will ensure effective tree shaking during build time.\n\n> In case your bundler supports package.json `exports` field - you can also omit `dist` in path folder `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/browser';`\n\n```ts\nimport { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/dist/browser';\n\nexport const monitoring = new BrowserMonitoringService({\n  authToken: NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN,\n  serviceName: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV,\n});\n```\n\nAs you can see we are using here all our exposed variables. If any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n**OPTIONAL: If you are using React you may benefit from utilizing [Error Boundaries](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary):**\n\n```tsx\nimport React, { Component, PropsWithChildren } from 'react';\nimport { monitoring } from '../services/monitoring';\n\ninterface ErrorBoundaryProps {}\ninterface ErrorBoundaryState {\n  hasError: boolean;\n}\n\nexport class ErrorBoundary extends Component<\n  PropsWithChildren<ErrorBoundaryProps>,\n  ErrorBoundaryState\n> {\n  static getDerivedStateFromError(_: Error): ErrorBoundaryState {\n    return { hasError: true };\n  }\n\n  state: ErrorBoundaryState = {\n    hasError: false,\n  };\n\n  componentDidCatch(error: Error) {\n    monitoring.reportError(error);\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return <h1>Sorry... There was an error</h1>;\n    }\n\n    return this.props.children;\n  }\n}\n```\n\n### Setup Server Monitoring (Next.js)\n\n<details>\n<summary>Approach with facade</summary>\n\n`initServerMonitoring` is a facade over `ServerMonitoringService`.\nYou can instantiate and use that service directly.\nThis function basically does one thing - instantiate it and can do 3 more additional things:\n\n- call `overrideNativeConsole` - method of the service to override native console, to log to datadog instead.\n- cal `catchProcessErrors` - method of the service to subscribe to native errors, to log them to datadog.\n- put service itself into global scope under defined name, so serverside code can use it.\n\nYou may wonder why we instantiate service here and not in server-side code.\nThe reason for that is if we override the native console and catch native errors - we would like to set up this as soon as possible.\nIf you don’t care too much about the very first seconds of next server - you can use alternative simpler server side logging solution.\n\nExample for Next.js:\n\n- update next.config.ts to include into start script of production server code\n  `next.config.ts:`\n\n```js\nconst {\n  initServerMonitoring,\n} = require('@kilohealth/web-app-monitoring/dist/server');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    const remoteMonitoringServiceParams = {\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    };\n    const config = {\n      shouldOverrideNativeConsole: true,\n      shouldCatchProcessErrors: true,\n      globalMonitoringInstanceName: 'kiloServerMonitoring',\n    };\n    initServerMonitoring(remoteMonitoringServiceParams, config);\n  }\n};\n```\n\n- update `custom.d.ts` file to declare that global scope now have monitoring service as a prop\n  In order to use ServerMonitoringService instance in other parts of code via global we need to let TS know\n  that we added new property to global object.\n  In Next.js you can just create or add next code into `custom.d.ts` file in root of the project.\n  Be aware that var name matches string that you provided in code above (kiloServerMonitoring in this case).\n  `custom.d.ts:`\n\n```ts\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\ndeclare global {\n  // eslint-disable-next-line no-var\n  var kiloServerMonitoring: ServerMonitoringService;\n}\n```\n\n- use it in code\n\n```ts\nexport const getHomeServerSideProps = async context => {\n  global.kiloServerMonitoring.info('getHomeServerSideProps called');\n};\n```\n\n</details>\n\n<details>\n<summary>Approach with direct instantiation</summary>\n\nIf you don’t care too much about catching native errors or native logs\nin the early stages of your server app -\nyou can avoid sharing logger via global scope and instead initialize it inside of app.\n\n```ts\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\nexport const monitoring = new ServerMonitoringService({\n  authToken: MONITORING_TOOL__API_KEY,\n  serviceName: MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: MONITORING_TOOL__SERVICE_ENV,\n});\n```\n\nAs you can see we are using here all our env variables.\nIf any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n</details>\n\n#### Init Tracing\n\nWe need to connect tracing as soon as possible during code, so it can be injected into all base modules for APM monitoring.\nTracing module is available via:\n\n```js\nconst {\n  initTracing,\n} = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\n\ninitTracing({\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n});\n```\n\nExample for Next.js:\n\n```js\nconst { PHASE_PRODUCTION_SERVER } = require('next/constants');\nconst {\n  initTracing,\n} = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    initTracing({\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    });\n  }\n};\n```\n\n> **Note:** In newer versions of Next.js there is experimental feature called\n> [instrumentationHook](https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation)\n> We can opt out from using undocumented `PHASE_PRODUCTION_SERVER` to use `instrumentationHook` for tracing init.\n> There is also possibility to use `NODE_OPTIONS='-r ./prestart-script.js ' next start` instead.\n> But there is an issue with `pino-datadog-transport`, which for performance reason spawns separate thread for log sending to data-dog and it this option seems to be passed to that process as well which triggers an infinite loop of require and initialization.\n\n## API\n\n### MonitoringService (both BrowserMonitoringService and ServerMonitoringService have these methods)\n\n<details>\n<summary>debug, info, warn</summary>\n\n```\ndebug(message: string, context?: object)\ninfo(message: string, context?: object)\nwarn(message: string, context?: object)\n```\n\n- `message` - any message to be logged\n- `context` - object with all needed and related to the log entrance data\n</details>\n\n<details>\n<summary>error</summary>\n\n```\nerror(message: string, context?: object, error?: Error)\n```\n\nSame as above, but you can also optionally pass error instance as third parameter\n\n</details>\n\n<details>\n<summary>reportError</summary>\n\n```\nreportError(error: Error, context?: object)\n```\n\nShortcut for `service.error()`, which uses `error.message` field as message param for `error` method.\n\n</details>\n\n### BrowserMonitoringService\n\n<details>\n<summary>constructor</summary>\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```ts\ninterface RemoteMonitoringServiceParams {\n  serviceName?: string;\n  serviceVersion?: string;\n  serviceEnv?: string;\n  authToken?: string;\n}\n```\n\n- `RemoteMonitoringServiceConfig` - datadog params passed to init function. More info in [docs](https://docs.datadoghq.com/logs/log_collection/javascript/#initialization-parameters)\n- `serviceName` - name of the service\n- `serviceVersion` - version of the service\n- `serviceEnv` - environment where service is deployed\n- `authToken` - client token\n</details>\n\n### ServerMonitoringService\n\n<details>\n<summary>constructor</summary>\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```ts\ninterface RemoteMonitoringServiceConfig {\n  transportOptions?: Partial<TransportBaseOptions>;\n  loggerOptions?: Partial<LoggerOptions>;\n}\n```\n\n- `transportOptions` - [pino-datadog-transport options](https://github.com/theogravity/pino-datadog-transport#configuration-options)\n- `loggerOptions` - [pino logger options](https://getpino.io/#/docs/api?id=options-object)\n</details>\n\n<details>\n<summary>overrideLogger</summary>\n\nOverrides logger passed as argument with monitoring logger.\nAll methods of this logger will be overridden with corresponding methods of server monitoring.\n\n```\noverrideLogger(unknownLogger: UnknownLogger)\n```\n\n```ts\ninterface UnknownLogger {\n  log?(...parts: unknown[]): void;\n  debug?(...parts: unknown[]): void;\n  info(...parts: unknown[]): void;\n  warn(...parts: unknown[]): void;\n  error(...parts: unknown[]): void;\n}\n```\n\n</details>\n\n<details>\n<summary>overrideNativeConsole</summary>\n\nCalls overrideLogger for native console.\n\n```\noverrideNativeConsole()\n```\n\n</details>\n\n<details>\n<summary>catchProcessErrors</summary>\n\nSubscribes to `unhandledRejection` and `uncaughtException` events of the process to report `error` in such cases.\n\n```ts\ncatchProcessErrors();\n```\n\n</details>\n\n### ServerMonitoringService\n\n<details>\n<summary>initServerMonitoring</summary>\n\nInstantiate ServerMonitoringService with provided params and may also do additional work,\ndepending on provided variables.\n\n```\ninitServerMonitoring = (\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  monitoringOptions?: MonitoringOptions,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig\n): ServerMonitoringService\n```\n\n```ts\ninterface MonitoringOptions {\n  shouldOverrideNativeConsole?: boolean;\n  shouldCatchProcessErrors?: boolean;\n  globalMonitoringInstanceName?: string;\n}\n```\n\n- `RemoteMonitoringServiceParams` and `RemoteMonitoringServiceConfig` are same as in `constructor` api\n- `shouldOverrideNativeConsole` - if `true`, will call `serverMonitoringService.overrideNativeConsole()` under the hood\n- `shouldCatchProcessErrors` - if `true`, will call `serverMonitoringService.catchProcessErrors()` under the hood\n- `globalMonitoringInstanceName` - if provided with non-empty string will put instantiated `serverMonitoringService` into global scope under provided name.\n\n</details>\n","readmeFilename":"README.md","gitHead":"eb0ab306faa0d27af387893baca9c90de7632531","description":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)","_nodeVersion":"18.17.1","_npmVersion":"9.8.1","dist":{"integrity":"sha512-HNXiXr8vFysaC/kIUcVo4ukt+jAS8kxwASu1eMscxeXNnTMK8DXrB8Ue76WEjz+AifgzdU7FveC0mjPgHB8dlA==","shasum":"bbc7bf43e51a0414fbca0209fd60389804a36e41","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-2.1.0-alpha.2.tgz","fileCount":40,"unpackedSize":54851,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC0Aktn04KQfh8seeHgcGX2Sr3EbYKOj1alOONerxa3rwIhAOD/a3qn1ekgROG6RRFTYxHL4ZzemVNFQz/LteZE4MpW"}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_2.1.0-alpha.2_1693826541309_0.7285619358983084"},"_hasShrinkwrap":false},"2.1.0":{"name":"@kilohealth/web-app-monitoring","version":"2.1.0","license":"MIT","author":{"name":"Kilo Health"},"sideEffects":false,"exports":{"./dist/server/initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./dist/server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./dist/browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","githooks:commit-msg":"commitlint --edit","githooks:pre-commit":"lint-staged","prepare":"husky install","semantic-release":"semantic-release","test":"jest"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^4.48.1","dd-trace":"^4.14.0","deepmerge":"^4.3.1","pino":"^8.15.0","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^17.7.1","@commitlint/config-conventional":"^17.7.0","@kilohealth/eslint-config-node":"^1.7.0","@kilohealth/prettier-config":"^1.0.1","@semantic-release/changelog":"^6.0.3","@semantic-release/git":"^10.0.1","@types/jest":"^29.5.4","@types/lodash":"^4.14.197","eslint":"^8.48.0","husky":"^8.0.3","jest":"^29.6.4","jest-environment-jsdom":"^29.6.4","jsdom":"^22.1.0","lint-staged":"^14.0.1","prettier":"^2.8.8","rimraf":"^5.0.1","semantic-release":"^21.1.1","sort-package-json":"^2.5.1","ts-jest":"^29.1.1","tslib":"^2.6.2","typescript":"^5.2.2"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_id":"@kilohealth/web-app-monitoring@2.1.0","gitHead":"2d15707cf8abfc50abc5c13edc841fd995685f1c","description":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)","_nodeVersion":"18.17.1","_npmVersion":"9.8.1","dist":{"integrity":"sha512-h+9O2x7pPXbUxTCOSp2C7AyHyeAWRTQpjgUetkDR8l4r8o0eW09nzDS15Rl9ma+LdZ4Vn6WBvNGl91+SsJ6GWQ==","shasum":"a7c1f86bc1c227d54380628100eee43f0767d30b","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-2.1.0.tgz","fileCount":40,"unpackedSize":54843,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHGMcVsHIuB1CEl6yGEznJ/dBPrcrEYXkZiM0gLQx6H7AiEA6BQG5Iv3PtzeZEyoZ9in0O3ZIhIV+u/T2IS8cUWW5JY="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_2.1.0_1693828573775_0.7974098261277982"},"_hasShrinkwrap":false},"2.1.1-alpha.1":{"name":"@kilohealth/web-app-monitoring","version":"2.1.1-alpha.1","license":"MIT","author":{"name":"Kilo Health"},"sideEffects":false,"exports":{"./dist/server/initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./dist/server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./dist/browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","githooks:commit-msg":"commitlint --edit","githooks:pre-commit":"lint-staged","prepare":"husky install","semantic-release":"semantic-release","test":"jest"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^5.1.0","dd-trace":"^4.17.0","deepmerge":"^4.3.1","pino":"^8.16.1","pino-datadog-transport":"^1.3.0"},"devDependencies":{"@commitlint/cli":"^18.0.0","@commitlint/config-conventional":"^18.0.0","@kilohealth/eslint-config-node":"^2.0.0","@kilohealth/prettier-config":"^2.0.0","@semantic-release/changelog":"^6.0.3","@semantic-release/git":"^10.0.1","@types/jest":"^29.5.6","eslint":"^8.52.0","husky":"^8.0.3","jest":"^29.7.0","jest-environment-jsdom":"^29.7.0","jsdom":"^22.1.0","lint-staged":"^15.0.2","prettier":"^3.0.3","rimraf":"^5.0.5","semantic-release":"^22.0.5","sort-package-json":"^2.6.0","ts-jest":"^29.1.1","tslib":"^2.6.2","typescript":"^5.2.2"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_id":"@kilohealth/web-app-monitoring@2.1.1-alpha.1","readme":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)\n\n# @kilohealth/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\n## The Idea\n\nPackage was created to abstract away underlying level of monitoring,\nto make it easy to setup monitoring for any env as well as to make it easy to migrate from DataDog to other monitoring solution.\n\nThe package consists of 3 parts:\n\n- Browser / Client monitoring (browser logs)\n- CLI (needed to upload sourcemaps for browser monitoring)\n- Server monitoring (server logs, APM, tracing)\n\n## Getting Started\n\n> **Note:** If you are migrating from direct datadog integration - don’t forget to remove `@datadog/...` dependencies. Those are now dependencies of `@kilohealth/web-app-monitoring`.\n>\n> ```\n> npm uninstall @datadog/...\n> ```\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n### Setup environment variables\n\n| Variable                           | Description                                                                                                                                                                                                                                              | Upload source maps | Server (APM, tracing) | Browser / Client |\n| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------: | :-------------------: | :--------------: |\n| `MONITORING_TOOL__API_KEY`         | This key is needed in order to uploaded source maps for browser monitoring, send server side (APM) logs and tracing info. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048). |         ✔️         |          ✔️           |                  |\n| `MONITORING_TOOL__SERVICE_NAME`    | The service name, for example: `timely-hand-web-funnel-app`.                                                                                                                                                                                             |         ✔️         |          ✔️           |        ✔️        |\n| `MONITORING_TOOL__SERVICE_VERSION` | The service version, for example: `$CI_COMMIT_SHA`.                                                                                                                                                                                                      |         ✔️         |          ✔️           |        ✔️        |\n| `MONITORING_TOOL__SERVICE_ENV`     | The service environment, for example: `$CI_ENVIRONMENT_NAME`.                                                                                                                                                                                            |         ✔️         |          ✔️           |        ✔️        |\n| `MONITORING_TOOL__CLIENT_TOKEN`    | This token is needed in order to send browser monitoring logs. You can create or find client token [here](https://app.datadoghq.com/organization-settings/client-tokens).                                                                                |         ️          |                       |        ✔️        |\n\n> **Note:** Depending on the framework you are using, in order to expose environment variables to the client you may need to prefix the environment variables as mentioned below:\n>\n> - For Next.js, add the prefix `NEXT_PUBLIC_` to each variable. Refer to the [documentation](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#bundling-environment-variables-for-the-browser) for more details.\n> - For Gatsby.js, add the prefix `GATSBY_` to each variable. Refer to the [documentation](https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/#accessing-environment-variables-in-the-browser) for more details.\n> - For Vite.js, add the prefix `VITE_` to each variable. Refer to the [documentation](https://vitejs.dev/guide/env-and-mode.html) for more details.\n\n> **Tip:** By following Single Source of Truth principle you can reexport variables, needed for the client, in the build stage (Next.js example):\n>\n> ```\n> NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME\n> NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION=$MONITORING_TOOL__SERVICE_VERSION\n> NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV=$MONITORING_TOOL__SERVICE_ENV\n> ```\n\n### Setup browser monitoring\n\n#### Generate hidden source maps\n\nIn order to upload source maps into the monitoring service we need to include those source map files into our build.\nThis can be done by slightly altering the build phase bundler configuration of our app:\n\n<details>\n<summary>Next.js (next.config.js)</summary>\n\n```js\nmodule.exports = {\n  webpack: (config, context) => {\n    const isClient = !context.isServer;\n    const isProd = !context.dev;\n    const isSourcemapsUploadEnabled = Boolean(\n      process.env.MONITORING_TOOL__API_KEY,\n    );\n\n    // Generate source maps only for the client side production build\n    if (isClient && isProd && isSourcemapsUploadEnabled) {\n      return {\n        ...config,\n        // No reference. No source maps exposure to the client (browser).\n        // Hidden source maps generation only for error reporting purposes.\n        devtool: 'hidden-source-map',\n      };\n    }\n\n    return config;\n  },\n};\n```\n\nRefer to the [documentation](https://webpack.js.org/configuration/devtool/) for more details.\n\n</details>\n\n<details>\n<summary>Gatsby.js (gatsby-node.js)</summary>\n\n```js\nmodule.exports = {\n  onCreateWebpackConfig: ({ stage, actions }) => {\n    const isSourcemapsUploadEnabled = Boolean(\n      process.env.MONITORING_TOOL__API_KEY,\n    );\n    // build-javascript is prod build phase\n    if (stage === 'build-javascript' && isSourcemapsUploadEnabled) {\n      actions.setWebpackConfig({\n        // No reference. No source maps exposure to the client (browser).\n        // Hidden source maps generation only for error reporting purposes.\n        devtool: 'hidden-source-map',\n      });\n    }\n  },\n};\n```\n\nRefer to the [documentation](https://webpack.js.org/configuration/devtool/) for more details.\n\n</details>\n\n<details>\n<summary>Vite.js (vite.config.js)</summary>\n\n```js\nexport default defineConfig({\n  build: {\n    // No reference. No source maps exposure to the client (browser).\n    // Hidden source maps generation only for error reporting purposes.\n    sourcemap: process.env.MONITORING_TOOL__API_KEY ? 'hidden' : false,\n  },\n});\n```\n\nRefer to the [documentation](https://vitejs.dev/config/build-options.html#build-sourcemap) for more details.\n\n</details>\n\n> **Note:** We are using `hidden source maps` only for error reporting purposes.\n> That means our source maps are not exposed to the client\n> and there are no references to those source maps in our source code.\n\n#### Upload generated source maps\n\nIn order to upload generated source maps into the monitoring service, you should use `web-app-monitoring__upload-sourcemaps` bin, provided by `@kilohealth/web-app-monitoring` package.\nTo run the script you need to provide arguments:\n\n| Argument               | Description                                                                                                                                                                                                                                                                                                                                                                                                                                             |   Vite   |                              Next                               |   Gatsby   |\n| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------: | :-------------------------------------------------------------: | :--------: |\n| `--buildDir` or `-d`   | This should be RELATIVE path to your build directory. For example `./dist` or `./build`.                                                                                                                                                                                                                                                                                                                                                                | `./dist` |                     `./.next/static/chunks`                     | `./public` |\n| `--publicPath` or `-p` | This is RELATIVE path, part of URL between domain (which can be different for different environments) and path to file itself. In other words - base path for all the assets within your application.<br/>You can think of this as kind of relative [Public Path](https://webpack.js.org/guides/public-path/). For example it can be `/` or `/static`. In other words this is common relative prefix for all your static files or `/` if there is none. |   `/`    | `/_next/static/chunks` (!!! `_` instead of `.` in file system)️ |    `/`     |\n\nScript example for Next.js:\n\n```\n\"scripts\": {\n  \"upload:sourcemaps\": \"web-app-monitoring__upload-sourcemaps --buildDir ./.next/static/chunks --publicPath /_next/static/chunks\",\n  ...\n},\n```\n\nAnd then your CI should run `upload:sourcemaps` script for the build that includes generated source maps.\n\n### Browser Monitoring Usage\n\n> **Important note:** There is no single entry point for package. You can't do something like `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring';`\n> Reason for that is to avoid bundling server-code into client bundle and vice versa. This structure will ensure effective tree shaking during build time.\n\n> In case your bundler supports package.json `exports` field - you can also omit `dist` in path folder `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/browser';`\n\n```ts\nimport { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/dist/browser';\n\nexport const monitoring = new BrowserMonitoringService({\n  authToken: NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN,\n  serviceName: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV,\n});\n```\n\nAs you can see we are using here all our exposed variables. If any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n**OPTIONAL: If you are using React you may benefit from utilizing [Error Boundaries](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary):**\n\n```tsx\nimport React, { Component, PropsWithChildren } from 'react';\nimport { monitoring } from '../services/monitoring';\n\ninterface ErrorBoundaryProps {}\ninterface ErrorBoundaryState {\n  hasError: boolean;\n}\n\nexport class ErrorBoundary extends Component<\n  PropsWithChildren<ErrorBoundaryProps>,\n  ErrorBoundaryState\n> {\n  static getDerivedStateFromError(_: Error): ErrorBoundaryState {\n    return { hasError: true };\n  }\n\n  state: ErrorBoundaryState = {\n    hasError: false,\n  };\n\n  componentDidCatch(error: Error) {\n    monitoring.reportError(error);\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return <h1>Sorry... There was an error</h1>;\n    }\n\n    return this.props.children;\n  }\n}\n```\n\n### Setup Server Monitoring (Next.js)\n\n<details>\n<summary>Approach with facade</summary>\n\n`initServerMonitoring` is a facade over `ServerMonitoringService`.\nYou can instantiate and use that service directly.\nThis function basically does one thing - instantiate it and can do 3 more additional things:\n\n- call `overrideNativeConsole` - method of the service to override native console, to log to datadog instead.\n- cal `catchProcessErrors` - method of the service to subscribe to native errors, to log them to datadog.\n- put service itself into global scope under defined name, so serverside code can use it.\n\nYou may wonder why we instantiate service here and not in server-side code.\nThe reason for that is if we override the native console and catch native errors - we would like to set up this as soon as possible.\nIf you don’t care too much about the very first seconds of next server - you can use alternative simpler server side logging solution.\n\nExample for Next.js:\n\n- update next.config.ts to include into start script of production server code\n  `next.config.ts:`\n\n```js\nconst {\n  initServerMonitoring,\n} = require('@kilohealth/web-app-monitoring/dist/server');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    const remoteMonitoringServiceParams = {\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    };\n    const config = {\n      shouldOverrideNativeConsole: true,\n      shouldCatchProcessErrors: true,\n      globalMonitoringInstanceName: 'kiloServerMonitoring',\n    };\n    initServerMonitoring(remoteMonitoringServiceParams, config);\n  }\n};\n```\n\n- update `custom.d.ts` file to declare that global scope now have monitoring service as a prop\n  In order to use ServerMonitoringService instance in other parts of code via global we need to let TS know\n  that we added new property to global object.\n  In Next.js you can just create or add next code into `custom.d.ts` file in root of the project.\n  Be aware that var name matches string that you provided in code above (kiloServerMonitoring in this case).\n  `custom.d.ts:`\n\n```ts\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\ndeclare global {\n  // eslint-disable-next-line no-var\n  var kiloServerMonitoring: ServerMonitoringService;\n}\n```\n\n- use it in code\n\n```ts\nexport const getHomeServerSideProps = async context => {\n  global.kiloServerMonitoring.info('getHomeServerSideProps called');\n};\n```\n\n</details>\n\n<details>\n<summary>Approach with direct instantiation</summary>\n\nIf you don’t care too much about catching native errors or native logs\nin the early stages of your server app -\nyou can avoid sharing logger via global scope and instead initialize it inside of app.\n\n```ts\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\nexport const monitoring = new ServerMonitoringService({\n  authToken: MONITORING_TOOL__API_KEY,\n  serviceName: MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: MONITORING_TOOL__SERVICE_ENV,\n});\n```\n\nAs you can see we are using here all our env variables.\nIf any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n</details>\n\n#### Init Tracing\n\nWe need to connect tracing as soon as possible during code, so it can be injected into all base modules for APM monitoring.\nTracing module is available via:\n\n```js\nconst {\n  initTracing,\n} = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\n\ninitTracing({\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n});\n```\n\nExample for Next.js:\n\n```js\nconst { PHASE_PRODUCTION_SERVER } = require('next/constants');\nconst {\n  initTracing,\n} = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    initTracing({\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    });\n  }\n};\n```\n\n> **Note:** In newer versions of Next.js there is experimental feature called\n> [instrumentationHook](https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation)\n> We can opt out from using undocumented `PHASE_PRODUCTION_SERVER` to use `instrumentationHook` for tracing init.\n> There is also possibility to use `NODE_OPTIONS='-r ./prestart-script.js ' next start` instead.\n> But there is an issue with `pino-datadog-transport`, which for performance reason spawns separate thread for log sending to data-dog and it this option seems to be passed to that process as well which triggers an infinite loop of require and initialization.\n\n## API\n\n### MonitoringService (both BrowserMonitoringService and ServerMonitoringService have these methods)\n\n<details>\n<summary>debug, info, warn</summary>\n\n```\ndebug(message: string, context?: object)\ninfo(message: string, context?: object)\nwarn(message: string, context?: object)\n```\n\n- `message` - any message to be logged\n- `context` - object with all needed and related to the log entrance data\n</details>\n\n<details>\n<summary>error</summary>\n\n```\nerror(message: string, context?: object, error?: Error)\n```\n\nSame as above, but you can also optionally pass error instance as third parameter\n\n</details>\n\n<details>\n<summary>reportError</summary>\n\n```\nreportError(error: Error, context?: object)\n```\n\nShortcut for `service.error()`, which uses `error.message` field as message param for `error` method.\n\n</details>\n\n### BrowserMonitoringService\n\n<details>\n<summary>constructor</summary>\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```ts\ninterface RemoteMonitoringServiceParams {\n  serviceName?: string;\n  serviceVersion?: string;\n  serviceEnv?: string;\n  authToken?: string;\n}\n```\n\n- `RemoteMonitoringServiceConfig` - datadog params passed to init function. More info in [docs](https://docs.datadoghq.com/logs/log_collection/javascript/#initialization-parameters)\n- `serviceName` - name of the service\n- `serviceVersion` - version of the service\n- `serviceEnv` - environment where service is deployed\n- `authToken` - client token\n</details>\n\n### ServerMonitoringService\n\n<details>\n<summary>constructor</summary>\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```ts\ninterface RemoteMonitoringServiceConfig {\n  transportOptions?: Partial<TransportBaseOptions>;\n  loggerOptions?: Partial<LoggerOptions>;\n}\n```\n\n- `transportOptions` - [pino-datadog-transport options](https://github.com/theogravity/pino-datadog-transport#configuration-options)\n- `loggerOptions` - [pino logger options](https://getpino.io/#/docs/api?id=options-object)\n</details>\n\n<details>\n<summary>overrideLogger</summary>\n\nOverrides logger passed as argument with monitoring logger.\nAll methods of this logger will be overridden with corresponding methods of server monitoring.\n\n```\noverrideLogger(unknownLogger: UnknownLogger)\n```\n\n```ts\ninterface UnknownLogger {\n  log?(...parts: unknown[]): void;\n  debug?(...parts: unknown[]): void;\n  info(...parts: unknown[]): void;\n  warn(...parts: unknown[]): void;\n  error(...parts: unknown[]): void;\n}\n```\n\n</details>\n\n<details>\n<summary>overrideNativeConsole</summary>\n\nCalls overrideLogger for native console.\n\n```\noverrideNativeConsole()\n```\n\n</details>\n\n<details>\n<summary>catchProcessErrors</summary>\n\nSubscribes to `unhandledRejection` and `uncaughtException` events of the process to report `error` in such cases.\n\n```ts\ncatchProcessErrors();\n```\n\n</details>\n\n### ServerMonitoringService\n\n<details>\n<summary>initServerMonitoring</summary>\n\nInstantiate ServerMonitoringService with provided params and may also do additional work,\ndepending on provided variables.\n\n```\ninitServerMonitoring = (\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  monitoringOptions?: MonitoringOptions,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig\n): ServerMonitoringService\n```\n\n```ts\ninterface MonitoringOptions {\n  shouldOverrideNativeConsole?: boolean;\n  shouldCatchProcessErrors?: boolean;\n  globalMonitoringInstanceName?: string;\n}\n```\n\n- `RemoteMonitoringServiceParams` and `RemoteMonitoringServiceConfig` are same as in `constructor` api\n- `shouldOverrideNativeConsole` - if `true`, will call `serverMonitoringService.overrideNativeConsole()` under the hood\n- `shouldCatchProcessErrors` - if `true`, will call `serverMonitoringService.catchProcessErrors()` under the hood\n- `globalMonitoringInstanceName` - if provided with non-empty string will put instantiated `serverMonitoringService` into global scope under provided name.\n\n</details>\n","readmeFilename":"README.md","gitHead":"14ae27b66f4ffdad1455244b85e92c445b2b3a9d","description":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)","_nodeVersion":"18.18.2","_npmVersion":"10.2.1","dist":{"integrity":"sha512-CNhPo3HjCFRgJSXlWt8rOpuJ8vhIXqZkpiN8zhACEe6Q1Jqrklj4kvSzlHmv8FCnnprUxWp2bB/DmX9BW/lwTg==","shasum":"bf16e0a57ba03064c2e953ab08bbdb54440c3ac1","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-2.1.1-alpha.1.tgz","fileCount":40,"unpackedSize":54816,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDGwWXwGOowbrLK1onaJlP13zxMPs4CciHwt18vwtZt+AiAH0qITFayCyNtaiBjZuh5Ap1AN5M+r22MYklKl5KzaYg=="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_2.1.1-alpha.1_1698155956872_0.0980457624135751"},"_hasShrinkwrap":false},"2.1.1-alpha.2":{"name":"@kilohealth/web-app-monitoring","version":"2.1.1-alpha.2","license":"MIT","author":{"name":"Kilo Health"},"sideEffects":false,"exports":{"./dist/server/initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./dist/server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./dist/browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","githooks:commit-msg":"commitlint --edit","githooks:pre-commit":"lint-staged","prepare":"husky install","semantic-release":"semantic-release","test":"jest"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^5.2.0","dd-trace":"^4.18.0","deepmerge":"^4.3.1","pino":"^8.16.1","pino-datadog-transport":"^1.3.2"},"devDependencies":{"@commitlint/cli":"^18.4.1","@commitlint/config-conventional":"^18.4.0","@kilohealth/eslint-config-node":"^2.0.0","@kilohealth/prettier-config":"^2.0.0","@semantic-release/changelog":"^6.0.3","@semantic-release/git":"^10.0.1","@types/jest":"^29.5.8","eslint":"^8.53.0","husky":"^8.0.3","jest":"^29.7.0","jest-environment-jsdom":"^29.7.0","jsdom":"^22.1.0","lint-staged":"^15.1.0","prettier":"^3.1.0","rimraf":"^5.0.5","semantic-release":"^22.0.7","sort-package-json":"^2.6.0","ts-jest":"^29.1.1","typescript":"^5.2.2"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_id":"@kilohealth/web-app-monitoring@2.1.1-alpha.2","readme":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)\n\n# @kilohealth/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\n## The Idea\n\nPackage was created to abstract away underlying level of monitoring,\nto make it easy to setup monitoring for any env as well as to make it easy to migrate from DataDog to other monitoring solution.\n\nThe package consists of 3 parts:\n\n- Browser / Client monitoring (browser logs)\n- CLI (needed to upload sourcemaps for browser monitoring)\n- Server monitoring (server logs, APM, tracing)\n\n## Getting Started\n\n> **Note:** If you are migrating from direct datadog integration - don’t forget to remove `@datadog/...` dependencies. Those are now dependencies of `@kilohealth/web-app-monitoring`.\n>\n> ```\n> npm uninstall @datadog/...\n> ```\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n### Setup environment variables\n\n| Variable                           | Description                                                                                                                                                                                                                                              | Upload source maps | Server (APM, tracing) | Browser / Client |\n| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------: | :-------------------: | :--------------: |\n| `MONITORING_TOOL__API_KEY`         | This key is needed in order to uploaded source maps for browser monitoring, send server side (APM) logs and tracing info. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048). |         ✔️         |          ✔️           |                  |\n| `MONITORING_TOOL__SERVICE_NAME`    | The service name, for example: `timely-hand-web-funnel-app`.                                                                                                                                                                                             |         ✔️         |          ✔️           |        ✔️        |\n| `MONITORING_TOOL__SERVICE_VERSION` | The service version, for example: `$CI_COMMIT_SHA`.                                                                                                                                                                                                      |         ✔️         |          ✔️           |        ✔️        |\n| `MONITORING_TOOL__SERVICE_ENV`     | The service environment, for example: `$CI_ENVIRONMENT_NAME`.                                                                                                                                                                                            |         ✔️         |          ✔️           |        ✔️        |\n| `MONITORING_TOOL__CLIENT_TOKEN`    | This token is needed in order to send browser monitoring logs. You can create or find client token [here](https://app.datadoghq.com/organization-settings/client-tokens).                                                                                |         ️          |                       |        ✔️        |\n\n> **Note:** Depending on the framework you are using, in order to expose environment variables to the client you may need to prefix the environment variables as mentioned below:\n>\n> - For Next.js, add the prefix `NEXT_PUBLIC_` to each variable. Refer to the [documentation](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#bundling-environment-variables-for-the-browser) for more details.\n> - For Gatsby.js, add the prefix `GATSBY_` to each variable. Refer to the [documentation](https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/#accessing-environment-variables-in-the-browser) for more details.\n> - For Vite.js, add the prefix `VITE_` to each variable. Refer to the [documentation](https://vitejs.dev/guide/env-and-mode.html) for more details.\n\n> **Tip:** By following Single Source of Truth principle you can reexport variables, needed for the client, in the build stage (Next.js example):\n>\n> ```\n> NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME\n> NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION=$MONITORING_TOOL__SERVICE_VERSION\n> NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV=$MONITORING_TOOL__SERVICE_ENV\n> ```\n\n### Setup browser monitoring\n\n#### Generate hidden source maps\n\nIn order to upload source maps into the monitoring service we need to include those source map files into our build.\nThis can be done by slightly altering the build phase bundler configuration of our app:\n\n<details>\n<summary>Next.js (next.config.js)</summary>\n\n```js\nmodule.exports = {\n  webpack: (config, context) => {\n    const isClient = !context.isServer;\n    const isProd = !context.dev;\n    const isSourcemapsUploadEnabled = Boolean(\n      process.env.MONITORING_TOOL__API_KEY,\n    );\n\n    // Generate source maps only for the client side production build\n    if (isClient && isProd && isSourcemapsUploadEnabled) {\n      return {\n        ...config,\n        // No reference. No source maps exposure to the client (browser).\n        // Hidden source maps generation only for error reporting purposes.\n        devtool: 'hidden-source-map',\n      };\n    }\n\n    return config;\n  },\n};\n```\n\nRefer to the [documentation](https://webpack.js.org/configuration/devtool/) for more details.\n\n</details>\n\n<details>\n<summary>Gatsby.js (gatsby-node.js)</summary>\n\n```js\nmodule.exports = {\n  onCreateWebpackConfig: ({ stage, actions }) => {\n    const isSourcemapsUploadEnabled = Boolean(\n      process.env.MONITORING_TOOL__API_KEY,\n    );\n    // build-javascript is prod build phase\n    if (stage === 'build-javascript' && isSourcemapsUploadEnabled) {\n      actions.setWebpackConfig({\n        // No reference. No source maps exposure to the client (browser).\n        // Hidden source maps generation only for error reporting purposes.\n        devtool: 'hidden-source-map',\n      });\n    }\n  },\n};\n```\n\nRefer to the [documentation](https://webpack.js.org/configuration/devtool/) for more details.\n\n</details>\n\n<details>\n<summary>Vite.js (vite.config.js)</summary>\n\n```js\nexport default defineConfig({\n  build: {\n    // No reference. No source maps exposure to the client (browser).\n    // Hidden source maps generation only for error reporting purposes.\n    sourcemap: process.env.MONITORING_TOOL__API_KEY ? 'hidden' : false,\n  },\n});\n```\n\nRefer to the [documentation](https://vitejs.dev/config/build-options.html#build-sourcemap) for more details.\n\n</details>\n\n> **Note:** We are using `hidden source maps` only for error reporting purposes.\n> That means our source maps are not exposed to the client\n> and there are no references to those source maps in our source code.\n\n#### Upload generated source maps\n\nIn order to upload generated source maps into the monitoring service, you should use `web-app-monitoring__upload-sourcemaps` bin, provided by `@kilohealth/web-app-monitoring` package.\nTo run the script you need to provide arguments:\n\n| Argument               | Description                                                                                                                                                                                                                                                                                                                                                                                                                                             |   Vite   |                              Next                               |   Gatsby   |\n| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------: | :-------------------------------------------------------------: | :--------: |\n| `--buildDir` or `-d`   | This should be RELATIVE path to your build directory. For example `./dist` or `./build`.                                                                                                                                                                                                                                                                                                                                                                | `./dist` |                     `./.next/static/chunks`                     | `./public` |\n| `--publicPath` or `-p` | This is RELATIVE path, part of URL between domain (which can be different for different environments) and path to file itself. In other words - base path for all the assets within your application.<br/>You can think of this as kind of relative [Public Path](https://webpack.js.org/guides/public-path/). For example it can be `/` or `/static`. In other words this is common relative prefix for all your static files or `/` if there is none. |   `/`    | `/_next/static/chunks` (!!! `_` instead of `.` in file system)️ |    `/`     |\n\nScript example for Next.js:\n\n```\n\"scripts\": {\n  \"upload:sourcemaps\": \"web-app-monitoring__upload-sourcemaps --buildDir ./.next/static/chunks --publicPath /_next/static/chunks\",\n  ...\n},\n```\n\nAnd then your CI should run `upload:sourcemaps` script for the build that includes generated source maps.\n\n### Browser Monitoring Usage\n\n> **Important note:** There is no single entry point for package. You can't do something like `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring';`\n> Reason for that is to avoid bundling server-code into client bundle and vice versa. This structure will ensure effective tree shaking during build time.\n\n> In case your bundler supports package.json `exports` field - you can also omit `dist` in path folder `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/browser';`\n\n```ts\nimport { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/dist/browser';\n\nexport const monitoring = new BrowserMonitoringService({\n  authToken: NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN,\n  serviceName: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV,\n});\n```\n\nAs you can see we are using here all our exposed variables. If any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n**OPTIONAL: If you are using React you may benefit from utilizing [Error Boundaries](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary):**\n\n```tsx\nimport React, { Component, PropsWithChildren } from 'react';\nimport { monitoring } from '../services/monitoring';\n\ninterface ErrorBoundaryProps {}\ninterface ErrorBoundaryState {\n  hasError: boolean;\n}\n\nexport class ErrorBoundary extends Component<\n  PropsWithChildren<ErrorBoundaryProps>,\n  ErrorBoundaryState\n> {\n  static getDerivedStateFromError(_: Error): ErrorBoundaryState {\n    return { hasError: true };\n  }\n\n  state: ErrorBoundaryState = {\n    hasError: false,\n  };\n\n  componentDidCatch(error: Error) {\n    monitoring.reportError(error);\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return <h1>Sorry... There was an error</h1>;\n    }\n\n    return this.props.children;\n  }\n}\n```\n\n### Setup Server Monitoring (Next.js)\n\n<details>\n<summary>Approach with facade</summary>\n\n`initServerMonitoring` is a facade over `ServerMonitoringService`.\nYou can instantiate and use that service directly.\nThis function basically does one thing - instantiate it and can do 3 more additional things:\n\n- call `overrideNativeConsole` - method of the service to override native console, to log to datadog instead.\n- cal `catchProcessErrors` - method of the service to subscribe to native errors, to log them to datadog.\n- put service itself into global scope under defined name, so serverside code can use it.\n\nYou may wonder why we instantiate service here and not in server-side code.\nThe reason for that is if we override the native console and catch native errors - we would like to set up this as soon as possible.\nIf you don’t care too much about the very first seconds of next server - you can use alternative simpler server side logging solution.\n\nExample for Next.js:\n\n- update next.config.ts to include into start script of production server code\n  `next.config.ts:`\n\n```js\nconst {\n  initServerMonitoring,\n} = require('@kilohealth/web-app-monitoring/dist/server');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    const remoteMonitoringServiceParams = {\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    };\n    const config = {\n      shouldOverrideNativeConsole: true,\n      shouldCatchProcessErrors: true,\n      globalMonitoringInstanceName: 'kiloServerMonitoring',\n    };\n    initServerMonitoring(remoteMonitoringServiceParams, config);\n  }\n};\n```\n\n- update `custom.d.ts` file to declare that global scope now have monitoring service as a prop\n  In order to use ServerMonitoringService instance in other parts of code via global we need to let TS know\n  that we added new property to global object.\n  In Next.js you can just create or add next code into `custom.d.ts` file in root of the project.\n  Be aware that var name matches string that you provided in code above (kiloServerMonitoring in this case).\n  `custom.d.ts:`\n\n```ts\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\ndeclare global {\n  // eslint-disable-next-line no-var\n  var kiloServerMonitoring: ServerMonitoringService;\n}\n```\n\n- use it in code\n\n```ts\nexport const getHomeServerSideProps = async context => {\n  global.kiloServerMonitoring.info('getHomeServerSideProps called');\n};\n```\n\n</details>\n\n<details>\n<summary>Approach with direct instantiation</summary>\n\nIf you don’t care too much about catching native errors or native logs\nin the early stages of your server app -\nyou can avoid sharing logger via global scope and instead initialize it inside of app.\n\n```ts\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\nexport const monitoring = new ServerMonitoringService({\n  authToken: MONITORING_TOOL__API_KEY,\n  serviceName: MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: MONITORING_TOOL__SERVICE_ENV,\n});\n```\n\nAs you can see we are using here all our env variables.\nIf any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n</details>\n\n#### Init Tracing\n\nWe need to connect tracing as soon as possible during code, so it can be injected into all base modules for APM monitoring.\nTracing module is available via:\n\n```js\nconst {\n  initTracing,\n} = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\n\ninitTracing({\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n});\n```\n\nExample for Next.js:\n\n```js\nconst { PHASE_PRODUCTION_SERVER } = require('next/constants');\nconst {\n  initTracing,\n} = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    initTracing({\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    });\n  }\n};\n```\n\n> **Note:** In newer versions of Next.js there is experimental feature called\n> [instrumentationHook](https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation)\n> We can opt out from using undocumented `PHASE_PRODUCTION_SERVER` to use `instrumentationHook` for tracing init.\n> There is also possibility to use `NODE_OPTIONS='-r ./prestart-script.js ' next start` instead.\n> But there is an issue with `pino-datadog-transport`, which for performance reason spawns separate thread for log sending to data-dog and it this option seems to be passed to that process as well which triggers an infinite loop of require and initialization.\n\n## API\n\n### MonitoringService (both BrowserMonitoringService and ServerMonitoringService have these methods)\n\n<details>\n<summary>debug, info, warn</summary>\n\n```\ndebug(message: string, context?: object)\ninfo(message: string, context?: object)\nwarn(message: string, context?: object)\n```\n\n- `message` - any message to be logged\n- `context` - object with all needed and related to the log entrance data\n</details>\n\n<details>\n<summary>error</summary>\n\n```\nerror(message: string, context?: object, error?: Error)\n```\n\nSame as above, but you can also optionally pass error instance as third parameter\n\n</details>\n\n<details>\n<summary>reportError</summary>\n\n```\nreportError(error: Error, context?: object)\n```\n\nShortcut for `service.error()`, which uses `error.message` field as message param for `error` method.\n\n</details>\n\n### BrowserMonitoringService\n\n<details>\n<summary>constructor</summary>\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```ts\ninterface RemoteMonitoringServiceParams {\n  serviceName?: string;\n  serviceVersion?: string;\n  serviceEnv?: string;\n  authToken?: string;\n}\n```\n\n- `RemoteMonitoringServiceConfig` - datadog params passed to init function. More info in [docs](https://docs.datadoghq.com/logs/log_collection/javascript/#initialization-parameters)\n- `serviceName` - name of the service\n- `serviceVersion` - version of the service\n- `serviceEnv` - environment where service is deployed\n- `authToken` - client token\n</details>\n\n### ServerMonitoringService\n\n<details>\n<summary>constructor</summary>\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```ts\ninterface RemoteMonitoringServiceConfig {\n  transportOptions?: Partial<TransportBaseOptions>;\n  loggerOptions?: Partial<LoggerOptions>;\n}\n```\n\n- `transportOptions` - [pino-datadog-transport options](https://github.com/theogravity/pino-datadog-transport#configuration-options)\n- `loggerOptions` - [pino logger options](https://getpino.io/#/docs/api?id=options-object)\n</details>\n\n<details>\n<summary>overrideLogger</summary>\n\nOverrides logger passed as argument with monitoring logger.\nAll methods of this logger will be overridden with corresponding methods of server monitoring.\n\n```\noverrideLogger(unknownLogger: UnknownLogger)\n```\n\n```ts\ninterface UnknownLogger {\n  log?(...parts: unknown[]): void;\n  debug?(...parts: unknown[]): void;\n  info(...parts: unknown[]): void;\n  warn(...parts: unknown[]): void;\n  error(...parts: unknown[]): void;\n}\n```\n\n</details>\n\n<details>\n<summary>overrideNativeConsole</summary>\n\nCalls overrideLogger for native console.\n\n```\noverrideNativeConsole()\n```\n\n</details>\n\n<details>\n<summary>catchProcessErrors</summary>\n\nSubscribes to `unhandledRejection` and `uncaughtException` events of the process to report `error` in such cases.\n\n```ts\ncatchProcessErrors();\n```\n\n</details>\n\n### ServerMonitoringService\n\n<details>\n<summary>initServerMonitoring</summary>\n\nInstantiate ServerMonitoringService with provided params and may also do additional work,\ndepending on provided variables.\n\n```\ninitServerMonitoring = (\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  monitoringOptions?: MonitoringOptions,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig\n): ServerMonitoringService\n```\n\n```ts\ninterface MonitoringOptions {\n  shouldOverrideNativeConsole?: boolean;\n  shouldCatchProcessErrors?: boolean;\n  globalMonitoringInstanceName?: string;\n}\n```\n\n- `RemoteMonitoringServiceParams` and `RemoteMonitoringServiceConfig` are same as in `constructor` api\n- `shouldOverrideNativeConsole` - if `true`, will call `serverMonitoringService.overrideNativeConsole()` under the hood\n- `shouldCatchProcessErrors` - if `true`, will call `serverMonitoringService.catchProcessErrors()` under the hood\n- `globalMonitoringInstanceName` - if provided with non-empty string will put instantiated `serverMonitoringService` into global scope under provided name.\n\n</details>\n","readmeFilename":"README.md","gitHead":"2a82892a28bf05ba6f1cd0dc26a98056a99f6f94","description":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)","_nodeVersion":"20.9.0","_npmVersion":"10.2.3","dist":{"integrity":"sha512-iLE8C1UQh3T9jzrkNOHJgeRvmiNPnp1in9gnwAW14FwjO6n9fvXi+56sD8Ra15uGG+z21cAiKVgU/Xcxc5lJmw==","shasum":"02e8c7c4f132140e43771cb177e33c2da34a7629","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-2.1.1-alpha.2.tgz","fileCount":40,"unpackedSize":58239,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDKceQ7RpPlAVURoxpmGVRszKT0o+L3ptxi9gwDPzbehAiA0/S5znmYUD6PpP0gYbs5Z+X/5a6z9QQMEmUCYJhR08w=="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_2.1.1-alpha.2_1699952967948_0.5318054485851305"},"_hasShrinkwrap":false},"2.1.1":{"name":"@kilohealth/web-app-monitoring","version":"2.1.1","license":"MIT","author":{"name":"Kilo Health"},"sideEffects":false,"exports":{"./dist/server/initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./dist/server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./dist/browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","githooks:commit-msg":"commitlint --edit","githooks:pre-commit":"lint-staged","prepare":"husky install","semantic-release":"semantic-release","test":"jest"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^5.2.0","dd-trace":"^4.18.0","deepmerge":"^4.3.1","pino":"^8.16.1","pino-datadog-transport":"^1.3.2"},"devDependencies":{"@commitlint/cli":"^18.4.1","@commitlint/config-conventional":"^18.4.0","@kilohealth/eslint-config-node":"^2.0.0","@kilohealth/prettier-config":"^2.0.0","@semantic-release/changelog":"^6.0.3","@semantic-release/git":"^10.0.1","@types/jest":"^29.5.8","eslint":"^8.53.0","husky":"^8.0.3","jest":"^29.7.0","jest-environment-jsdom":"^29.7.0","jsdom":"^22.1.0","lint-staged":"^15.1.0","prettier":"^3.1.0","rimraf":"^5.0.5","semantic-release":"^22.0.7","sort-package-json":"^2.6.0","ts-jest":"^29.1.1","typescript":"^5.2.2"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_id":"@kilohealth/web-app-monitoring@2.1.1","gitHead":"15d2b4f7d01c08ed40ca8af90878c29030041255","description":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)","_nodeVersion":"20.9.0","_npmVersion":"10.2.3","dist":{"integrity":"sha512-1sbFozamQl3gd29kvFT+VSlozxF3WnvDsWDObF6tq2k8yzxDMlRpSDemrYoQ+KnTGPxMUZfz2PF6pG654Qa1Kg==","shasum":"d0928854696c19b07ee6c63da5caaa73b0c457b2","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-2.1.1.tgz","fileCount":40,"unpackedSize":58231,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD7kuw/xXfzfWupGqnWAi+KdO6weTYI1P3/gG5TZ1kGogIhANIx35aOKF968Karm3tpzoJKr0gUTme/MQkPDQsNm+gb"}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_2.1.1_1699966104100_0.199362485648938"},"_hasShrinkwrap":false},"2.1.2":{"name":"@kilohealth/web-app-monitoring","version":"2.1.2","license":"MIT","author":{"name":"Kilo Health"},"sideEffects":false,"exports":{"./dist/server/initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./dist/server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./dist/browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./initTracing":{"import":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"},"require":{"types":"./dist/server/initTracing.d.ts","default":"./dist/server/initTracing.js"}},"./server":{"import":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"},"require":{"types":"./dist/server/index.d.ts","default":"./dist/server/index.js"}},"./browser":{"import":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"require":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}}},"bin":{"web-app-monitoring__upload-sourcemaps":"dist/cli/sourcemap-uploader.sh"},"scripts":{"build":"rimraf dist && tsc && npm run build:cli","build:cli":"cp -r ./src/cli ./dist/cli","githooks:commit-msg":"commitlint --edit","githooks:pre-commit":"lint-staged","prepare":"husky install","semantic-release":"semantic-release","test":"jest"},"lint-staged":{"package.json":["sort-package-json"],"*.{json,md,yml}":["prettier --write"],"*.{ts,tsx,js,jsx}":["prettier --write","eslint --max-warnings=0 --fix"]},"prettier":"@kilohealth/prettier-config","eslintConfig":{"extends":"@kilohealth/eslint-config-node"},"dependencies":{"@datadog/browser-logs":"^5.2.0","dd-trace":"^4.20.0","deepmerge":"^4.3.1","pino":"^8.16.1","pino-datadog-transport":"^1.3.2"},"devDependencies":{"@commitlint/cli":"^18.4.1","@commitlint/config-conventional":"^18.4.0","@kilohealth/eslint-config-node":"^2.0.0","@kilohealth/prettier-config":"^2.0.0","@semantic-release/changelog":"^6.0.3","@semantic-release/git":"^10.0.1","@types/jest":"^29.5.8","eslint":"^8.53.0","husky":"^8.0.3","jest":"^29.7.0","jest-environment-jsdom":"^29.7.0","jsdom":"^22.1.0","lint-staged":"^15.1.0","prettier":"^3.1.0","rimraf":"^5.0.5","semantic-release":"^22.0.7","sort-package-json":"^2.6.0","ts-jest":"^29.1.1","typescript":"^5.2.2"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_id":"@kilohealth/web-app-monitoring@2.1.2","gitHead":"13aceea40cc0cd4a0d4a5b8fdefa4f82d4a53ebf","description":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)","_nodeVersion":"20.10.0","_npmVersion":"10.2.3","dist":{"integrity":"sha512-9qZLNr+J8rSjtMhDY5o8tzZicttx0622f6FUGL9sch1lOCiQ6TzIH/8iNRuGh9lK0h4y0z2KhwOVX1vbXM2Dsw==","shasum":"d0ff9818ee81d21caee1b8bcf2a0ce389a0adad0","tarball":"https://registry.npmjs.org/@kilohealth/web-app-monitoring/-/web-app-monitoring-2.1.2.tgz","fileCount":40,"unpackedSize":58231,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCODE0tuwlHpANOwq0FGdNph9BewHIu0vmGtT15IrnO1QIgFDYJleAfXbLv9CqmTXYcUwBmx81Sq6btZLht1qD96H0="}]},"_npmUser":{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},"directories":{},"maintainers":[{"name":"lukas.skamarakas","email":"lukas.skamarakas@kilo.health"},{"name":"alarm109","email":"matas109@gmail.com"},{"name":"lukebars","email":"taztdaras@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/web-app-monitoring_2.1.2_1701932645898_0.6245799331731583"},"_hasShrinkwrap":false}},"time":{"created":"2023-05-23T03:17:55.471Z","1.0.0":"2023-05-23T03:17:55.721Z","modified":"2024-01-03T08:39:31.932Z","1.0.0-alpha.1":"2023-05-23T03:40:10.387Z","1.0.0-alpha.2":"2023-05-23T04:26:10.174Z","1.0.0-alpha.3":"2023-05-24T06:03:05.950Z","1.0.0-alpha.4":"2023-05-24T06:35:40.762Z","1.0.0-alpha.5":"2023-05-29T06:19:17.902Z","1.0.0-alpha.6":"2023-05-29T06:21:59.429Z","1.0.0-alpha.7":"2023-05-29T07:22:51.928Z","1.0.0-alpha.8":"2023-05-30T07:03:54.335Z","1.0.0-alpha.9":"2023-05-30T09:50:03.101Z","1.0.0-alpha.10":"2023-05-30T11:45:46.888Z","1.0.0-alpha.11":"2023-05-31T11:24:32.346Z","1.0.0-alpha.12":"2023-06-02T04:38:46.151Z","1.0.0-alpha.13":"2023-06-16T05:02:18.742Z","1.0.0-alpha.14":"2023-06-16T06:53:22.673Z","1.0.0-alpha.15":"2023-06-21T02:45:15.517Z","1.0.0-alpha.16":"2023-06-21T04:28:16.094Z","1.0.0-beta.1":"2023-06-21T08:28:51.682Z","1.1.0":"2023-06-21T09:33:58.201Z","1.2.0-alpha.1":"2023-06-23T06:44:39.533Z","1.2.0-alpha.2":"2023-06-23T11:38:48.414Z","1.2.0":"2023-06-23T12:23:37.362Z","1.2.1-alpha.1":"2023-06-30T13:32:05.032Z","1.2.1-alpha.2":"2023-06-30T17:42:05.153Z","1.2.1-alpha.3":"2023-06-30T18:29:11.407Z","1.2.1":"2023-07-04T09:25:59.791Z","1.2.1-alpha.4":"2023-07-05T03:28:05.281Z","1.2.2-alpha.1":"2023-07-05T03:52:12.702Z","1.2.2-alpha.2":"2023-07-06T05:13:29.366Z","1.2.2":"2023-07-06T05:18:31.300Z","1.3.0-alpha.1":"2023-07-14T05:04:16.620Z","1.3.0-alpha.2":"2023-07-14T05:08:42.560Z","2.0.0-alpha.1":"2023-07-14T06:26:48.600Z","2.0.0":"2023-07-14T06:59:40.105Z","2.0.1-alpha.1":"2023-08-20T10:18:03.308Z","2.0.1":"2023-08-21T06:19:58.501Z","2.0.2-alpha.1":"2023-09-01T13:46:15.903Z","2.0.2":"2023-09-04T08:10:24.701Z","2.1.0-alpha.1":"2023-09-04T09:52:47.911Z","2.1.0-alpha.2":"2023-09-04T11:22:21.481Z","2.1.0":"2023-09-04T11:56:13.998Z","2.1.1-alpha.1":"2023-10-24T13:59:17.144Z","2.1.1-alpha.2":"2023-11-14T09:09:28.097Z","2.1.1":"2023-11-14T12:48:24.274Z","2.1.2":"2023-12-07T07:04:06.145Z"},"maintainers":[{"email":"itops@kilo.health","name":"kiloit"},{"email":"lukas.skamarakas@kilo.health","name":"lukas.skamarakas"},{"email":"matas109@gmail.com","name":"alarm109"},{"email":"taztdaras@gmail.com","name":"lukebars"}],"description":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)","author":{"name":"Kilo Health"},"license":"MIT","readme":"[![SWUbanner](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-no-action.svg)](https://stand-with-ukraine.pp.ua)\n\n# @kilohealth/web-app-monitoring\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE.md)\n\n## The Idea\n\nPackage was created to abstract away underlying level of monitoring,\nto make it easy to setup monitoring for any env as well as to make it easy to migrate from DataDog to other monitoring solution.\n\nThe package consists of 3 parts:\n\n- Browser / Client monitoring (browser logs)\n- CLI (needed to upload sourcemaps for browser monitoring)\n- Server monitoring (server logs, APM, tracing)\n\n## Getting Started\n\n> **Note:** If you are migrating from direct datadog integration - don’t forget to remove `@datadog/...` dependencies. Those are now dependencies of `@kilohealth/web-app-monitoring`.\n>\n> ```\n> npm uninstall @datadog/...\n> ```\n\n### Install package\n\n```\nnpm install @kilohealth/web-app-monitoring\n```\n\n### Setup environment variables\n\n| Variable                           | Description                                                                                                                                                                                                                                              | Upload source maps | Server (APM, tracing) | Browser / Client |\n| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------: | :-------------------: | :--------------: |\n| `MONITORING_TOOL__API_KEY`         | This key is needed in order to uploaded source maps for browser monitoring, send server side (APM) logs and tracing info. You can find API key [here](https://app.datadoghq.com/organization-settings/api-keys?id=97403b1a-0806-45e1-9ecb-fa059af82048). |         ✔️         |          ✔️           |                  |\n| `MONITORING_TOOL__SERVICE_NAME`    | The service name, for example: `timely-hand-web-funnel-app`.                                                                                                                                                                                             |         ✔️         |          ✔️           |        ✔️        |\n| `MONITORING_TOOL__SERVICE_VERSION` | The service version, for example: `$CI_COMMIT_SHA`.                                                                                                                                                                                                      |         ✔️         |          ✔️           |        ✔️        |\n| `MONITORING_TOOL__SERVICE_ENV`     | The service environment, for example: `$CI_ENVIRONMENT_NAME`.                                                                                                                                                                                            |         ✔️         |          ✔️           |        ✔️        |\n| `MONITORING_TOOL__CLIENT_TOKEN`    | This token is needed in order to send browser monitoring logs. You can create or find client token [here](https://app.datadoghq.com/organization-settings/client-tokens).                                                                                |         ️          |                       |        ✔️        |\n\n> **Note:** Depending on the framework you are using, in order to expose environment variables to the client you may need to prefix the environment variables as mentioned below:\n>\n> - For Next.js, add the prefix `NEXT_PUBLIC_` to each variable. Refer to the [documentation](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#bundling-environment-variables-for-the-browser) for more details.\n> - For Gatsby.js, add the prefix `GATSBY_` to each variable. Refer to the [documentation](https://www.gatsbyjs.com/docs/how-to/local-development/environment-variables/#accessing-environment-variables-in-the-browser) for more details.\n> - For Vite.js, add the prefix `VITE_` to each variable. Refer to the [documentation](https://vitejs.dev/guide/env-and-mode.html) for more details.\n\n> **Tip:** By following Single Source of Truth principle you can reexport variables, needed for the client, in the build stage (Next.js example):\n>\n> ```\n> NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME=$MONITORING_TOOL__SERVICE_NAME\n> NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION=$MONITORING_TOOL__SERVICE_VERSION\n> NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV=$MONITORING_TOOL__SERVICE_ENV\n> ```\n\n### Setup browser monitoring\n\n#### Generate hidden source maps\n\nIn order to upload source maps into the monitoring service we need to include those source map files into our build.\nThis can be done by slightly altering the build phase bundler configuration of our app:\n\n<details>\n<summary>Next.js (next.config.js)</summary>\n\n```js\nmodule.exports = {\n  webpack: (config, context) => {\n    const isClient = !context.isServer;\n    const isProd = !context.dev;\n    const isSourcemapsUploadEnabled = Boolean(\n      process.env.MONITORING_TOOL__API_KEY,\n    );\n\n    // Generate source maps only for the client side production build\n    if (isClient && isProd && isSourcemapsUploadEnabled) {\n      return {\n        ...config,\n        // No reference. No source maps exposure to the client (browser).\n        // Hidden source maps generation only for error reporting purposes.\n        devtool: 'hidden-source-map',\n      };\n    }\n\n    return config;\n  },\n};\n```\n\nRefer to the [documentation](https://webpack.js.org/configuration/devtool/) for more details.\n\n</details>\n\n<details>\n<summary>Gatsby.js (gatsby-node.js)</summary>\n\n```js\nmodule.exports = {\n  onCreateWebpackConfig: ({ stage, actions }) => {\n    const isSourcemapsUploadEnabled = Boolean(\n      process.env.MONITORING_TOOL__API_KEY,\n    );\n    // build-javascript is prod build phase\n    if (stage === 'build-javascript' && isSourcemapsUploadEnabled) {\n      actions.setWebpackConfig({\n        // No reference. No source maps exposure to the client (browser).\n        // Hidden source maps generation only for error reporting purposes.\n        devtool: 'hidden-source-map',\n      });\n    }\n  },\n};\n```\n\nRefer to the [documentation](https://webpack.js.org/configuration/devtool/) for more details.\n\n</details>\n\n<details>\n<summary>Vite.js (vite.config.js)</summary>\n\n```js\nexport default defineConfig({\n  build: {\n    // No reference. No source maps exposure to the client (browser).\n    // Hidden source maps generation only for error reporting purposes.\n    sourcemap: process.env.MONITORING_TOOL__API_KEY ? 'hidden' : false,\n  },\n});\n```\n\nRefer to the [documentation](https://vitejs.dev/config/build-options.html#build-sourcemap) for more details.\n\n</details>\n\n> **Note:** We are using `hidden source maps` only for error reporting purposes.\n> That means our source maps are not exposed to the client\n> and there are no references to those source maps in our source code.\n\n#### Upload generated source maps\n\nIn order to upload generated source maps into the monitoring service, you should use `web-app-monitoring__upload-sourcemaps` bin, provided by `@kilohealth/web-app-monitoring` package.\nTo run the script you need to provide arguments:\n\n| Argument               | Description                                                                                                                                                                                                                                                                                                                                                                                                                                             |   Vite   |                              Next                               |   Gatsby   |\n| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------: | :-------------------------------------------------------------: | :--------: |\n| `--buildDir` or `-d`   | This should be RELATIVE path to your build directory. For example `./dist` or `./build`.                                                                                                                                                                                                                                                                                                                                                                | `./dist` |                     `./.next/static/chunks`                     | `./public` |\n| `--publicPath` or `-p` | This is RELATIVE path, part of URL between domain (which can be different for different environments) and path to file itself. In other words - base path for all the assets within your application.<br/>You can think of this as kind of relative [Public Path](https://webpack.js.org/guides/public-path/). For example it can be `/` or `/static`. In other words this is common relative prefix for all your static files or `/` if there is none. |   `/`    | `/_next/static/chunks` (!!! `_` instead of `.` in file system)️ |    `/`     |\n\nScript example for Next.js:\n\n```\n\"scripts\": {\n  \"upload:sourcemaps\": \"web-app-monitoring__upload-sourcemaps --buildDir ./.next/static/chunks --publicPath /_next/static/chunks\",\n  ...\n},\n```\n\nAnd then your CI should run `upload:sourcemaps` script for the build that includes generated source maps.\n\n### Browser Monitoring Usage\n\n> **Important note:** There is no single entry point for package. You can't do something like `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring';`\n> Reason for that is to avoid bundling server-code into client bundle and vice versa. This structure will ensure effective tree shaking during build time.\n\n> In case your bundler supports package.json `exports` field - you can also omit `dist` in path folder `import { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/browser';`\n\n```ts\nimport { BrowserMonitoringService } from '@kilohealth/web-app-monitoring/dist/browser';\n\nexport const monitoring = new BrowserMonitoringService({\n  authToken: NEXT_PUBLIC_MONITORING_TOOL__CLIENT_TOKEN,\n  serviceName: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: NEXT_PUBLIC_MONITORING_TOOL__SERVICE_ENV,\n});\n```\n\nAs you can see we are using here all our exposed variables. If any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n**OPTIONAL: If you are using React you may benefit from utilizing [Error Boundaries](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary):**\n\n```tsx\nimport React, { Component, PropsWithChildren } from 'react';\nimport { monitoring } from '../services/monitoring';\n\ninterface ErrorBoundaryProps {}\ninterface ErrorBoundaryState {\n  hasError: boolean;\n}\n\nexport class ErrorBoundary extends Component<\n  PropsWithChildren<ErrorBoundaryProps>,\n  ErrorBoundaryState\n> {\n  static getDerivedStateFromError(_: Error): ErrorBoundaryState {\n    return { hasError: true };\n  }\n\n  state: ErrorBoundaryState = {\n    hasError: false,\n  };\n\n  componentDidCatch(error: Error) {\n    monitoring.reportError(error);\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return <h1>Sorry... There was an error</h1>;\n    }\n\n    return this.props.children;\n  }\n}\n```\n\n### Setup Server Monitoring (Next.js)\n\n<details>\n<summary>Approach with facade</summary>\n\n`initServerMonitoring` is a facade over `ServerMonitoringService`.\nYou can instantiate and use that service directly.\nThis function basically does one thing - instantiate it and can do 3 more additional things:\n\n- call `overrideNativeConsole` - method of the service to override native console, to log to datadog instead.\n- cal `catchProcessErrors` - method of the service to subscribe to native errors, to log them to datadog.\n- put service itself into global scope under defined name, so serverside code can use it.\n\nYou may wonder why we instantiate service here and not in server-side code.\nThe reason for that is if we override the native console and catch native errors - we would like to set up this as soon as possible.\nIf you don’t care too much about the very first seconds of next server - you can use alternative simpler server side logging solution.\n\nExample for Next.js:\n\n- update next.config.ts to include into start script of production server code\n  `next.config.ts:`\n\n```js\nconst {\n  initServerMonitoring,\n} = require('@kilohealth/web-app-monitoring/dist/server');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    const remoteMonitoringServiceParams = {\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    };\n    const config = {\n      shouldOverrideNativeConsole: true,\n      shouldCatchProcessErrors: true,\n      globalMonitoringInstanceName: 'kiloServerMonitoring',\n    };\n    initServerMonitoring(remoteMonitoringServiceParams, config);\n  }\n};\n```\n\n- update `custom.d.ts` file to declare that global scope now have monitoring service as a prop\n  In order to use ServerMonitoringService instance in other parts of code via global we need to let TS know\n  that we added new property to global object.\n  In Next.js you can just create or add next code into `custom.d.ts` file in root of the project.\n  Be aware that var name matches string that you provided in code above (kiloServerMonitoring in this case).\n  `custom.d.ts:`\n\n```ts\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\ndeclare global {\n  // eslint-disable-next-line no-var\n  var kiloServerMonitoring: ServerMonitoringService;\n}\n```\n\n- use it in code\n\n```ts\nexport const getHomeServerSideProps = async context => {\n  global.kiloServerMonitoring.info('getHomeServerSideProps called');\n};\n```\n\n</details>\n\n<details>\n<summary>Approach with direct instantiation</summary>\n\nIf you don’t care too much about catching native errors or native logs\nin the early stages of your server app -\nyou can avoid sharing logger via global scope and instead initialize it inside of app.\n\n```ts\nimport { ServerMonitoringService } from '@kilohealth/web-app-monitoring/dist/server';\n\nexport const monitoring = new ServerMonitoringService({\n  authToken: MONITORING_TOOL__API_KEY,\n  serviceName: MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: MONITORING_TOOL__SERVICE_ENV,\n});\n```\n\nAs you can see we are using here all our env variables.\nIf any of these is not defined - the service will fall back to console.log and warn you there about it.\nNow you can just use it like\n\n```\nmonitoring.info('Monitoring service initialized');\n```\n\n</details>\n\n#### Init Tracing\n\nWe need to connect tracing as soon as possible during code, so it can be injected into all base modules for APM monitoring.\nTracing module is available via:\n\n```js\nconst {\n  initTracing,\n} = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\n\ninitTracing({\n  serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n  serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n  serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n  authToken: process.env.MONITORING_TOOL__API_KEY,\n});\n```\n\nExample for Next.js:\n\n```js\nconst { PHASE_PRODUCTION_SERVER } = require('next/constants');\nconst {\n  initTracing,\n} = require('@kilohealth/web-app-monitoring/dist/server/initTracing');\n\nmodule.exports = phase => {\n  if (phase === PHASE_PRODUCTION_SERVER) {\n    initTracing({\n      serviceName: process.env.MONITORING_TOOL__SERVICE_NAME,\n      serviceVersion: process.env.MONITORING_TOOL__SERVICE_VERSION,\n      serviceEnv: process.env.MONITORING_TOOL__SERVICE_ENV,\n      authToken: process.env.MONITORING_TOOL__API_KEY,\n    });\n  }\n};\n```\n\n> **Note:** In newer versions of Next.js there is experimental feature called\n> [instrumentationHook](https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation)\n> We can opt out from using undocumented `PHASE_PRODUCTION_SERVER` to use `instrumentationHook` for tracing init.\n> There is also possibility to use `NODE_OPTIONS='-r ./prestart-script.js ' next start` instead.\n> But there is an issue with `pino-datadog-transport`, which for performance reason spawns separate thread for log sending to data-dog and it this option seems to be passed to that process as well which triggers an infinite loop of require and initialization.\n\n## API\n\n### MonitoringService (both BrowserMonitoringService and ServerMonitoringService have these methods)\n\n<details>\n<summary>debug, info, warn</summary>\n\n```\ndebug(message: string, context?: object)\ninfo(message: string, context?: object)\nwarn(message: string, context?: object)\n```\n\n- `message` - any message to be logged\n- `context` - object with all needed and related to the log entrance data\n</details>\n\n<details>\n<summary>error</summary>\n\n```\nerror(message: string, context?: object, error?: Error)\n```\n\nSame as above, but you can also optionally pass error instance as third parameter\n\n</details>\n\n<details>\n<summary>reportError</summary>\n\n```\nreportError(error: Error, context?: object)\n```\n\nShortcut for `service.error()`, which uses `error.message` field as message param for `error` method.\n\n</details>\n\n### BrowserMonitoringService\n\n<details>\n<summary>constructor</summary>\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```ts\ninterface RemoteMonitoringServiceParams {\n  serviceName?: string;\n  serviceVersion?: string;\n  serviceEnv?: string;\n  authToken?: string;\n}\n```\n\n- `RemoteMonitoringServiceConfig` - datadog params passed to init function. More info in [docs](https://docs.datadoghq.com/logs/log_collection/javascript/#initialization-parameters)\n- `serviceName` - name of the service\n- `serviceVersion` - version of the service\n- `serviceEnv` - environment where service is deployed\n- `authToken` - client token\n</details>\n\n### ServerMonitoringService\n\n<details>\n<summary>constructor</summary>\n\n```\nconstructor(\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig,\n)\n```\n\n```ts\ninterface RemoteMonitoringServiceConfig {\n  transportOptions?: Partial<TransportBaseOptions>;\n  loggerOptions?: Partial<LoggerOptions>;\n}\n```\n\n- `transportOptions` - [pino-datadog-transport options](https://github.com/theogravity/pino-datadog-transport#configuration-options)\n- `loggerOptions` - [pino logger options](https://getpino.io/#/docs/api?id=options-object)\n</details>\n\n<details>\n<summary>overrideLogger</summary>\n\nOverrides logger passed as argument with monitoring logger.\nAll methods of this logger will be overridden with corresponding methods of server monitoring.\n\n```\noverrideLogger(unknownLogger: UnknownLogger)\n```\n\n```ts\ninterface UnknownLogger {\n  log?(...parts: unknown[]): void;\n  debug?(...parts: unknown[]): void;\n  info(...parts: unknown[]): void;\n  warn(...parts: unknown[]): void;\n  error(...parts: unknown[]): void;\n}\n```\n\n</details>\n\n<details>\n<summary>overrideNativeConsole</summary>\n\nCalls overrideLogger for native console.\n\n```\noverrideNativeConsole()\n```\n\n</details>\n\n<details>\n<summary>catchProcessErrors</summary>\n\nSubscribes to `unhandledRejection` and `uncaughtException` events of the process to report `error` in such cases.\n\n```ts\ncatchProcessErrors();\n```\n\n</details>\n\n### ServerMonitoringService\n\n<details>\n<summary>initServerMonitoring</summary>\n\nInstantiate ServerMonitoringService with provided params and may also do additional work,\ndepending on provided variables.\n\n```\ninitServerMonitoring = (\n  remoteMonitoringServiceParams?: RemoteMonitoringServiceParams,\n  monitoringOptions?: MonitoringOptions,\n  remoteMonitoringServiceConfig?: RemoteMonitoringServiceConfig\n): ServerMonitoringService\n```\n\n```ts\ninterface MonitoringOptions {\n  shouldOverrideNativeConsole?: boolean;\n  shouldCatchProcessErrors?: boolean;\n  globalMonitoringInstanceName?: string;\n}\n```\n\n- `RemoteMonitoringServiceParams` and `RemoteMonitoringServiceConfig` are same as in `constructor` api\n- `shouldOverrideNativeConsole` - if `true`, will call `serverMonitoringService.overrideNativeConsole()` under the hood\n- `shouldCatchProcessErrors` - if `true`, will call `serverMonitoringService.catchProcessErrors()` under the hood\n- `globalMonitoringInstanceName` - if provided with non-empty string will put instantiated `serverMonitoringService` into global scope under provided name.\n\n</details>\n","readmeFilename":"README.md"}