{"_id":"@prftesp/cle-logger","_rev":"107-ea60381fad3696d2e11b1be723d77354","name":"@prftesp/cle-logger","dist-tags":{"latest":"2.3.1","beta":"2.3.1-beta.20240131.3"},"versions":{"0.0.1":{"name":"@prftesp/cle-logger","version":"0.0.1","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","format":"prettier --write \"src/**/*.ts\" \"src/**/*.js\"","lint":"tslint -p tsconfig.json","prepare":"npm run build","prepublishOnly":"npm run format && npm run lint && npm test","publish":"npm publish --access public"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@types/uuid":"^3.4.4","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","tslint":"^5.18.0","tslint-config-prettier":"^1.18.0","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0","uuid":"^3.3.2"},"_id":"@prftesp/cle-logger@0.0.1","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-clqluqAPMmWoY6JiB3JDTjwd6qnW7yJeb1DQgB0G1heeDxL9ZFTe1eGjcANENo2cDqQcG3+MxxQ9Lep3Okzb6Q==","shasum":"835e6ccdaaca535d7aa21717301d65b495c04dc9","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.1.tgz","fileCount":3,"unpackedSize":4606,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdE+6pCRA9TVsSAnZWagAAN5EP/20eU/vioHwb6mwkf5tt\nAFd+uH+cKF56oxSWWfhdiP8MJ2FlWTm+9uimJnmaxOEq3ofpvMAT7xkTpHfo\ntR4S5btFxQTyMZ4cs9bS8QjgEmrsKSmVnVZECcLy5j0ON1x9PWZ+pdk5WOCq\nkDNJohW7uIpfy6Q0Un2xisKbuiw94KeRc65W1W5vTy54Ex76NutFy/mgZkkK\nTb2YnWzrefnhvCebad/N7oZixqeaKczH7Ik4ZyRswd2aFKb2+fFciLUV2SRh\nvp8uKhicqYOJWKj7Zc21SkA835Fib7JpA7kQZ0/CBE2JRGlteL9JAxFVKt3k\n/yT2UvDRsuPx5O2cr3BhLhlTedj6G7DY3YqjMc2MUB0thhQUeaqhihPqxThe\nvXoAyWW8D276vahZl+/t3dX6q+ILihLijC2WVilToVNmIWECotaE4WU7x0pK\n8f0yZiuvUInzZJv2NmUwaW1haWvWvT7uT1ZTW68KJ2lBfFYuk0HYDNiTYaSd\ndHMlW4Rjw6ose9lMQATFmXV27pJc49r3wHoYZ5BL6QVYsckijjjxz1knnNoz\nXYJ5KQVayQUguxUdSacMT5iN2vsYsll9x8q6GMakYU4oVTvnB9EkYjeI/Grq\nox4x4gQk7wPobX2jjsSvW7lmtqR9nVDifZgyoaL6O8pIv0gaxlSbJvjLSRII\nyke2\r\n=6E8F\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICq7YUakfSGBRMOxtmq8fa2AaWMnCGycvCuB3teMb0fpAiEA5S2SdY70520rpLMGTto8pJ77r2b6YMV9NWLDJw5XTl0="}]},"maintainers":[{"name":"mikefrank","email":"mike.frank@perficient.com"},{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.1_1561587368924_0.03541866700371443"},"_hasShrinkwrap":false},"0.0.2":{"name":"@prftesp/cle-logger","version":"0.0.2","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","format":"prettier --write \"src/**/*.ts\" \"src/**/*.js\"","lint":"tslint -p tsconfig.json","prepare":"npm run build","prepublishOnly":"npm run format && npm run lint","publish":"npm publish --access public"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@types/uuid":"^3.4.4","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","tslint":"^5.18.0","tslint-config-prettier":"^1.18.0","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0","uuid":"^3.3.2"},"_id":"@prftesp/cle-logger@0.0.2","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-1RZws8+lQjaA7YBKBvIIxyOMrVqYHHGknw18XHe+CRA+bndp3bjXPpgppnQAuc05KqJIED7dSR1cEZSL+TaWcA==","shasum":"338418f161bcb52c14b082a99d1453f29de4adae","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.2.tgz","fileCount":3,"unpackedSize":4594,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdE+7pCRA9TVsSAnZWagAAwIgP/31kfBHVdPM9UcIfpvBJ\np89WAVeet7EZVFoqqz1HXbSKBwAaL7ZkLHR9aMc679B3QPglCQhQCH/InT7P\n9GGNQVmq/ZeyA9iXxMMLHwnbuM+qTOw5LtNfxtYwNLH6Q2hW8ezjTBDk9YET\nSq0F6Aovt4NOvkHxyMSxPc3+gWtRAFHIDuQPoyfisuGx8S8urwmjyp+gAHYy\n5imP3hsYcHpTdf3FDPzouEnAwBpqD6KbbhBfEcNbgPedTmdT/k6TVHc8XeuS\nVrXMVMks8F/vGkFJvac/Junw7/FC+aemeVyMweb5gKWL46xuVUpez1J8QeTn\nR6DBXD5dg4D3gb5XaV/HAb+v+pZ59M0bTmun5MkPIuxD0+jztZuN6nyEzHM9\nWfuMDABsFowrzNzmZLZJgQRyK/AX9eipiAUD0ULceBPOhtUaT/PJ7JZYXGsH\nXNo4rGaMZtEyMzugWyyNVsuPnfTIMQcb181TfgAb0mEpfStkWcl49F1W7OS8\nN49BHHJ9JUE45y39XMrRpgf0K2ZTGqHSVmd7y9kM8+8DCquahUtdcEMSveua\n5eZxiRgxIZDjDeda2mTnUR9ghyKmJGVQTfKddqsXpvXXa0hlA/OnsXFI9IjI\nCyTE9gd5h/+uaha680qquHdlXAjRkD62LJRcU1w8K3dEiXHI3fI5WVd+YcuD\nFIsa\r\n=bXZ+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDHMW9owoAEbeiAwpqe82yR9dgOuxoTSZfnJZIOH1B5AAIgCA2OohM7KRKd9auJdVxfpDJ63BCcgRaRSJOSZ38wSek="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.2_1561587432995_0.2765596826080303"},"_hasShrinkwrap":false},"0.0.3":{"name":"@prftesp/cle-logger","version":"0.0.3","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","format":"prettier --write \"src/**/*.ts\" \"src/**/*.js\"","lint":"tslint -p tsconfig.json","prepare":"npm run build","publish":"npm run format && npm run lint && npm test && npm publish --access public"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@types/uuid":"^3.4.4","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","tslint":"^5.18.0","tslint-config-prettier":"^1.18.0","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0","uuid":"^3.3.2"},"_id":"@prftesp/cle-logger@0.0.3","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-MV+L+daw7mawyBuPXIGlJZ4SM1Ep+ERe8IsN2LqdCHGtJXSC5UGpPSlaUgpsCo97FKHuqucdr2Qhm+gGo7rUDg==","shasum":"92ee670fa2d9ffa215280cc625442bb715ef3545","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.3.tgz","fileCount":3,"unpackedSize":4583,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdE+8sCRA9TVsSAnZWagAA5V0P/i0OcpXkL3DK2qXMgntI\nXgh+0KujNGhUIi3x4DkYTUEEgMtNuaLtwyLTQFeI5krVRCq6D4jEg6LDssDB\nLZ+QINjAifNZknb/A1Qt/N74baFvY/G7L2zn9JWTQbThRkaLV2/zxbwX2xSb\nCMLNHqBnEKXdTPn7MA0eJKfN9YBwLLTHRlccNSwFQwusQ473nQyz+44j+Lsr\n/3cB94v8GglVNOEwj2TaxpVahfo3hBI26bMO2zWzU9IMOKstVXnNcLzBgnaC\nv+WVuc50BkUz0PwyyUYlQYlVXAWR6vm/NVII/7/djnGgB0hp5xSW08m6kOe2\nbpDrtWDagr4NglIOT92gg4mDAC20oBKW+SfcMuTF9vzMa4ETvv4blyq/MqHA\nypdi871udtMeqGm8rauoXRyXKoisofvvkmtcTheP42riAa1ODMV3H6mcJ76j\nWjNyoaGlB3Vp/jejKMa5k6AsoIaedqBdjudeYfAjs7pJ7aLxYt0P346smiZG\n7IRQNuhiHcThBSXr02KE8w7ZkV78tgBhKGY4UatUow4lEGPnLpImCFgIocHn\n4//cQWhQhljUJfyPt5/Y+olEPy3HhYv9pptADdBpxl7FyXYuxJj5keq8PgSn\n+vqXTKIf8ES15hzc38/93Spd4fucHjEM7TiHMv81Ah1t8dhEuajJGiCDU8qg\njwV+\r\n=nK9N\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDG9ICjf/IXqETRRmuL/DdZz+VOGz14UupxU/4T1xYI+AiBwa06YneHYAKM6mpayIlQfKXMbUgcAAPYmEHdFaxvQWQ=="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.3_1561587499409_0.654592200964198"},"_hasShrinkwrap":false},"0.0.4":{"name":"@prftesp/cle-logger","version":"0.0.4","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","format":"prettier --write \"src/**/*.ts\" \"src/**/*.js\"","lint":"tslint -p tsconfig.json","publish":"npm run build && npm run format && npm run lint && npm test && npm publish --access public"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@types/uuid":"^3.4.4","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","tslint":"^5.18.0","tslint-config-prettier":"^1.18.0","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0","uuid":"^3.3.2"},"_id":"@prftesp/cle-logger@0.0.4","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-dDwHwIQ17LCqrC8CQmU6x2igiecR1ANmuz22OY/SzBhJqPc7GDeHzW1lKB24yFA1LwADip0vtjaLC2zYNLknng==","shasum":"8e74be6169744080617019eadd5e87723ca47b23","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.4.tgz","fileCount":3,"unpackedSize":4567,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdE/AFCRA9TVsSAnZWagAAFiEP/1JOXPKhD3WolTomQ+Y6\nZfakT/hWNk4C8bGfgQvV29CSwNeFFinimrBG2FHNr9msL9odigGhtgpR6EHV\nhRMvOAnE2LfSXqs7v6atQnBbAvlhhYGwb+M4GBowRXDCc1lGdg2uvgMsJmmK\n0dKZmY5oX/xe8Xz4gBUFMKnIhcVsdmgwMoKjZrzNDu4knvi6q6WsqGdc1dYO\neYujTB3Y8yc05NibJr/rN/mVtmyovGikDcGBT3B4Alj2kWmPIcRemEeV5jzF\ntxRWDaj00sFCyP9gXqhE3b01Rcq1H6BqjT5RSZ13JdBRuRjSEuXAFtBHk7Q4\nkYMSNNPtzWpatIpxxW53PzvaejqUf7+mFGOk2NCLuO/I3pA4/6pneQ/2gNcc\nZvlz+e/DCwmZO9jTyNm0lXpU7GopaU9zAI44HG95UKi6d5ELB6ooRYzmsXjw\ngUT5D1JLhq/NP0/wgRrwgKkhbEOyBLI8pcGFok0sHdRYvsF4agJGCZZFWvFF\ntJd1FoSuXcWaG96g7DL1oooftoNM2Ns2fNdPYwMCt4UtqNbZwyEqQCqXxX/U\nMQxbk8frxlCaqeExzaNLjyLCjgmHMurEZlkpjXRQzf1borXapzVVfKaGf4LD\nivB+0Hhiaj6UfcRMsCS22uEVfFVlSBNvkAeIJvO/JcehmbNza39Er7skVGgl\nvoYZ\r\n=dg0k\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCq66Uw8/t+iXhG5FflSlW5Yy6Bic/eOiQ/c3nuf168hwIhAL4FWDiEPzKSpRYfryRxi7s9h1EAciImQdCJ7vNm2aLD"}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.4_1561587717033_0.9748199463974558"},"_hasShrinkwrap":false},"0.0.5":{"name":"@prftesp/cle-logger","version":"0.0.5","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","format":"prettier --write \"src/**/*.ts\" \"src/**/*.js\"","lint":"tslint -p tsconfig.json","publish":"npm run build && npm run format && npm run lint && npm test && npm publish --access public"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@types/uuid":"^3.4.4","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","tslint":"^5.18.0","tslint-config-prettier":"^1.18.0","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0","uuid":"^3.3.2"},"_id":"@prftesp/cle-logger@0.0.5","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-P1vGSErChbq3w2/cfGs0VQzbCEO7smsbtFwRO/Dw81K/dMe0ntYsf5WJyVrjiP+gs4iWbRLziU1X6CcDomuSMg==","shasum":"f1c95979c0420b32e1d4e8b2e8c655cf3cc2f1ec","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.5.tgz","fileCount":3,"unpackedSize":4567,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdE/BeCRA9TVsSAnZWagAAztUQAJpfqZm2F/ggZZx60TcI\nSTSMX0SFxt08bNWe+OCe5fzTckwqJpdfn8O5DzWbUOXpea+e111T99QEkPoF\n34GGMAQjyReRY7x2dcD7SYDdDCepdRMVic0xICF3Zp45zuJhNe8VaLwHYjPa\nTHt7l38s2qxxu33maYcxA6BhgjheFEt+ZNs2UpmygbtoAEHVQH4OXlQg9hTv\nkb27Iwo7rTyqU1xuLyTBoMISM8mnL/iXI1vkTF/g998YE7FbOzLKEwudD350\nz4AJqQezrnkv++j/CzvE2nP2f/TE82MAsGESkCASGWSTi+M1/vvqPOOI1EIz\nhq4r9KShb9hxKfZKV4e0X9o60fLL+GxrYtEfjoAaW9/NyTqGouKLTR0+nN1A\nCVsvhxNJ3Nmksb+RaaAY/Z37qcBElMxUutIA+2Px5gi6bA2DFdsAxAJpY+CG\nuAeFecc9QaXKtafjAnUC18EZQYe8+qc1RDsJRlk8a1eMAygxoQmk0pv8ZBrN\nOQLKk1l+M+USkd/bPPyd/x+QJdhrPNnxOmle0XYaj6lfeEr6/FL2LG0JYmUC\nCqVYiDtLR08owp9cPw8MwrS1yFIbhK2F9Uk2d+3RMfawaHSzFd951/8AVjYl\n8TtE/K3uXFZBEP27z/bBSi2e5GsbQ28YgReo0WAwEv+/o9kSZBz/H73Xbadt\n3KQb\r\n=D/Kf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEZrZU2Fg7sp/CjVX/02zNUJZRtPxyZGjmy2EFrj+JoqAiEApFP8vqm68zPd+bZ00ewQHHd+556Dvwv18Ga9GHrAmo0="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.5_1561587805874_0.7808165612468208"},"_hasShrinkwrap":false},"0.0.6":{"name":"@prftesp/cle-logger","version":"0.0.6","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","format":"prettier --write \"src/**/*.ts\" \"src/**/*.js\"","lint":"tslint -p tsconfig.json","publish-lib":"npm run build && npm run format && npm run lint && npm test && npm publish --access public"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@types/uuid":"^3.4.4","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","tslint":"^5.18.0","tslint-config-prettier":"^1.18.0","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0","uuid":"^3.3.2"},"_id":"@prftesp/cle-logger@0.0.6","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-SzjdxK1YAdY9m0Sz1GIaF8BZCH++ByQnQSsPUM0Bj5IkpcTfYVscjOIAsJFSxHqPUZwqQXRPxq9y9fAzhqJk1w==","shasum":"f2c5cd1761c608d68d8335a61ddf07ce877557b3","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.6.tgz","fileCount":3,"unpackedSize":4571,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdE/CeCRA9TVsSAnZWagAAawQQAI10QejjaV7irkOJG6km\nvryLAQxP2iijxM/IOiu1VhcZ51YdPc7tm3VQNKJ0benGBptxBFUz85GGDgCY\nmDn+4FkMZ5IN08ZS5kbRglVzMCKWTiB9aImJAtvOocmvLvh1/Ttrwe3HNDIM\nzFrwuvsutNuYBFLGDGxELov+o+YUmpnW7QlvES7J6fNRMAvWhUPVbcit3EKm\nN+NY7XEFLbzWoeRZLWv0PfcjeYXG18a2JAf/UXuSa+LemUFmi5MxxXl1w/va\nb4z6wApH+EEGxQzAafpU8VTZqVwmR3yU0kq/kd66iSpgOlnp6p4kYUN496x5\nxTU2yjMvKs/0a3lIgbY1dzI1E4bhy3cyePotSmQTHsMZ9C0R3NWrQ1ocH+z+\nbELHEi8z/jMnd1tD/AR69BQgXnWDESTfjo7Km867OQyESUHE+2ibgwklA4BF\n0enrKyFHpCEq0iSDqY19z7+Axrf7Oq7KMhFmN1am6qgpcTMQI/vNSb8f+tzx\nVG05DoFB50eVlyhHzkQG62X8lpWV5rA0FRxt+UXJG9XuPqUGP3gV/BkGt7MR\n/fMHdKmVHcCrgJXMU2K3qgcmo0GRRJ3sduiAqYYdd/USyiccnsszap+oQqu5\nMuo/De5dInxH/l9kY+dpF4VsFjzKxoj9C9jXKM+I+VvYhiASoowSOb6HbI8l\nh7Vh\r\n=IQMw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCy5dScMGVsxcs8l83qq+jBbye3L2kp+ICkOkNoHAVxYQIhAOke7WeD64Srk+y2jN/ZScXhCd0jhE2/9hzKPIpysGee"}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.6_1561587870128_0.2620190953931869"},"_hasShrinkwrap":false},"0.0.7":{"name":"@prftesp/cle-logger","version":"0.0.7","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","format-check":"prettier --check \"src/**/*.ts\" \"src/**/*.js\"","format":"prettier --write \"src/**/*.ts\" \"src/**/*.js\"","lint":"tslint -p tsconfig.json","publish-lib":"npm run build && npm run format && npm run lint && npm test && npm publish --access public"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","tslint":"^5.18.0","tslint-config-prettier":"^1.18.0","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@0.0.7","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-xb6GMpvd4cCRBOVHnRsoqHzYrWhZOxrprS8r/Mf2XO7eUOrLSz4j++1n0JxedrVtnfXsixiQj50zRS2wnKXmeA==","shasum":"612a7884ba4e1b41cac9f9f6c5eb7a62041e3880","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.7.tgz","fileCount":3,"unpackedSize":5875,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdFjWSCRA9TVsSAnZWagAAgUMP/AlAeZofMYBCxuCRNBZa\n1c7RovECcL7k+lkmkc3MDEEg5q+8Avczr2o1wmfbvCLt7gsbn+bFPO7mrZbY\njNJo2wo14Hwx5GyS76qqY6XjLn5TanjBvJdbrD7YdlNCkQQKfFSrere7jrb4\nVRG95goFdFZVSARf7Bk57F671WJzGjYR5FLL9rlsWIHNKtuC8piWXl/9GKLM\no9uwNI6OvTpBciTb4IXutGN7QlYuGjAEQEa1VolB1xT7m+MSzuX4DAJ4wN3C\nO2/EXtw91dkGGofvqpUpYhPJ18dFMXpUreK3fN0i5EPhzUpRgxwnNMlmzF8n\ntRfODTfRu+vhvhOhLz5gh9hU9fUWbuT+NVbSBfdB7ZoqilJycc3cewSUcPCR\n/5U6JuX5xQriFgUScIUmkVHHhcI5YzZ9ek2KPTUUlv1C7MchBd4wbFk0h7qW\nu6NqcKXFEsjDAE0Qo0E1ozNhD4/WqVie2WZjCYSGrVh5byyinu0tYMXTGNLr\niAfgOamXf7sqzLEWauFtSvquVjBBhRRMR99gl65ApqocpAV8w8eKuxCJ09Xw\ndW7gY8aisNk0EGaElh86jG2IsuDpP6Y0Feu4prqmlRvCQM+y5gltY1lTe0MC\nvIWHxXvGBYR7IZXa8cU+etvlSCg1AaY6hth8g2MbqOY/hMWYfhaQMWqA/r3k\nLTVH\r\n=9dQk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICQlIaPOaNKYEyYL1J+S2ocSNlFxALnjYt8BgBDiaMS0AiEA3OAfp5G1Fs5KnT+sZCybY21QC0u5dq7zGxvYQ44ntIk="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.7_1561736593504_0.8054406552305864"},"_hasShrinkwrap":false},"0.0.8":{"name":"@prftesp/cle-logger","version":"0.0.8","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","format-check":"prettier --check \"src/**/*.ts\" \"src/**/*.js\"","format":"prettier --write \"src/**/*.ts\" \"src/**/*.js\"","lint":"tslint -p tsconfig.json","publish-lib":"npm run build && npm run format && npm run lint && npm test && npm publish --access public"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","tslint":"^5.18.0","tslint-config-prettier":"^1.18.0","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@0.0.8","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-OpT/FN9x6/ejI2bOAxSmVu/dpAur0My4O8I/YCWEU0ejpCeMwdQ41dOMSEtg9exUoUig7h6Hx6tujNw/RFjH7g==","shasum":"9bb83a352bb6c957264a2cc8733aae864c4c8e0b","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.8.tgz","fileCount":14,"unpackedSize":26716,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdFjaPCRA9TVsSAnZWagAAimsP/0m0hGxeyhR8WeJTwh7f\nTzv13exKN6c7/qqiMBAtAxD9ZJqhsZROIblnhOWmtHq0satkjf0JG4KaEJh6\nT2dLWTCEDYWZgFnuo7GLlbBeBaL6ysdmQVQTCp6SzoZBdaBGTpszZkbWdzcl\noB9pQjPUMhWAkeHWAmX6wF9fExubYYfAN0HFho/BSvVwKOaOkh5vBqJsZRtQ\nZxzeWha8a1ajO4avDT6BmLVK6XZzPh2Rbu15MrNoKXwlYdB1xzVecjei0g3k\nRgP1dEGj2YgV/mug9/VPzbhnJtGq1z4ab7yiPkBzLPlsK/2KMkJaQ/PrncXn\nL+gR1OQRvVHEC/sTxPRM6bwLavXyKiGg4eg7qROyYkVfS5gyxsamwa5Af/4F\nxDEhk7O5I0uCQ4rwMRvFKsLWu2mPqD89FyfGxYYJ8Ea7NSvplRrWORd5zw8E\neNsbI+qnCqLbD6r0finRMpKhChYVJUjyMhWGqK7UyzWSR/eaFXgeY3slcEJe\nJ0L4VdZsZ2/kFOQ7QJtNvoCp10hRJuWIJ1Tcy1uMRSm6E90tN6L79XybWy4M\nd3orfi7XGlr9xd/pnVTE5UlgDoi1ppDXqvxIcLcCyrcKSbtVCogw3KRj+bbW\noh1/bkB8rsSZ6hWcNdX8E5ti5poXz0pdfObiDUWTTXVVk6bDPYOIfDAeXyU4\njh/m\r\n=9xdk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDzyEkUIj2dNYMeErEryZ3cVU9rAcn6BNVqnvLU8QAUMAiAW9+tmwHW/qO1/eBDSe1foXaXSRbpUYpceoQQV2Ptwsw=="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.8_1561736847147_0.05076239077758493"},"_hasShrinkwrap":false},"0.0.9":{"name":"@prftesp/cle-logger","version":"0.0.9","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","format-check":"prettier --check \"src/**/*.ts\" \"src/**/*.js\"","format":"prettier --write \"src/**/*.ts\" \"src/**/*.js\"","lint":"tslint -p tsconfig.json","ci-check":"npm run build && npm run format-check && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","tslint":"^5.18.0","tslint-config-prettier":"^1.18.0","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿# Perficient.CLE.NodeLogger\r\n\r\n\r\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@0.0.9","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-F0jULyDXnzbb9pMvHH1oGphI0fhokgef53G+GSTL79ltRBpICO6WJFk0ox0hB/4mT+Q8UXe9XFnhq8AlRUbC8g==","shasum":"beb2e88a9aca925afae75dae4f0f1d1f853ab015","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.9.tgz","fileCount":14,"unpackedSize":27223,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdH0xMCRA9TVsSAnZWagAAVY8P/0mAQqBFYIn7+lFWqF7Z\noRFp1S52fqEbVwINuZDYPZAEHLYB2kS1vCKhtYlhb48twnJc8om8qSZ8xWou\n2dWxDY/4R9vWXSMjMp31PsUhmSbmv67mCIc8D090bDVBKDNUHvwq6/ZETENz\ndkIWSpCP7puStLwda/KEPUk5lXO++FcXsPkP+oJFdEfsyPz9mjT7rwbNuecA\nWGQ0tFqwrG3gnlfc5148t9ggpn/fEshivMcR4CJMFPN23rYmeQ2EURER/by2\nRNRnfvaexUx0i9ywvsSEO9d4M1ySkzAi/zFAamppU/5Qwx4mhEYws28zUN5a\nZkSznY9T/B/wqdavhzl5vvdEl4GDhAjgm6hKrFWZPn9sbHj2wOdQM9JUdkrI\numQ4F+485J9fe9OD1I17vPiAbzLCDMyoKbp0AZtzUtvf1TAgvD0gO3Dgi6Sy\nSnZyOZeAzaFNAwzKaAuBZPvIKvUy6cgCzvzxvkNgjPWPhQro9N6LfpK2qSQO\nWGG+5LPiMgZc+3EeJ+B5WwitFCmdJ0dY5bp8O0K2nJC3j12x9i1tZmvde/CB\noHhFetmf5RXhD71znHNf2O0DWGby/z3trouQKqVv+u21R7E5dUJZDZhCOOGk\nsmkmNOdGZzReTTLGwBnLKxYwUGAis3hB2NDL+2mOuOjC8ucpijASS2vMkfIa\ni27f\r\n=crtW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDRSdzpil44pR8VJereSim4Dx8FLHeQB2Jjm3pkLUe8QwIgHqBiuXUpvafY5Qt5wan0eNx74SiPGSJwfcv+npQWjDU="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.9_1562332235603_0.9387090852505366"},"_hasShrinkwrap":false},"0.0.11-beta.1691":{"name":"@prftesp/cle-logger","version":"0.0.11-beta.1691","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","format-check":"prettier --check \"src/**/*.ts\" \"src/**/*.js\"","format":"prettier --write \"src/**/*.ts\" \"src/**/*.js\"","lint":"tslint -p tsconfig.json","ci-check":"npm run build && npm run format-check && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","tslint":"^5.18.0","tslint-config-prettier":"^1.18.0","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿# Perficient.CLE.NodeLogger\n\n\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@0.0.11-beta.1691","_nodeVersion":"10.16.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-LLg2qnX9atPDzRM7C62O281oui2bp8QP+U4Cw7wQ24SaVBSGS/AmXVoUIVNbtGjNeltp9bIq0owKpfQVSgw9Zw==","shasum":"4cd834b7c14a67c01765e0197071f12c097b8cfc","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.11-beta.1691.tgz","fileCount":14,"unpackedSize":29474,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdH3jOCRA9TVsSAnZWagAAGJIP/ArGcjscnnBjKCZwFbSs\neN4rq0pZ2zTY+H2qcg6QsBHEs9oRb8x8FDjG3RXUe1P2l/llc8v1eeon0JCu\nosryeZPUVfIF3njvJWlKB4S+fktT3Xh19dv+OhED0nOSA9wZZ6+3/AufkQtU\nB95oUmMY+ULBjjMf74D/gRezHAv9wL9n+2DqnObgq2vD0vaEImJtfDeUut90\ngVIE5aTjchm0teKzJxyvr2HrYA31RYwl3hIHxC/P/Aw+r30MiSGwA0+POOSh\nHHFRp6MSW8Rb+AlB/JF1JY1+3GR6iCn5UvmtqE4ltdLNRxt/gKclgb3JHD+i\necOoEm9r8GQq1ccGhQrCHlXGz7rkpBUCoq/qHwx21oOXG/TEQacuA0xDlBv6\nqqI8oDOhdnUdLgsLOQ2A91jDGEI3FoZ3PTr8T/eiwe6dpwk397+QIxTtQStG\nCd6qy7Rxwbpv3ORLlFF3thmMEhBnIL42NBwL1g3joooLBKk1T5HsuzWJQRUg\nn2ti1aDftPJPros0PkZL6q8BJFkBSu91zYlKjKzaW7eeh7KknrDFPfmu6vPw\nRJufS5lS7EAkep4WPu54sjxG2ZntFc+p8+UkBg1YBx0uJOrRH7wQ1HID87FV\nakk8zJwMIGpLqPOeMqch/aYjKdlp7+fL/xi+LdHsLkufwTGxYAWq8SH74f0+\nE7tM\r\n=yPhx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAOofAQjU11geHFJIcwPwEdHFjARG/lMEUht5T9V1+KIAiEAxqvy16FKRIME8mgJw/yV98vZdul+PK+U3dVFmuw1MQ8="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.11-beta.1691_1562343629492_0.3225054059080663"},"_hasShrinkwrap":false},"0.0.11-beta.1696":{"name":"@prftesp/cle-logger","version":"0.0.11-beta.1696","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","format-check":"prettier --check \"src/**/*.ts\" \"src/**/*.js\"","format":"prettier --write \"src/**/*.ts\" \"src/**/*.js\"","lint":"tslint -p tsconfig.json","ci-check":"npm run build && npm run format-check && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","tslint":"^5.18.0","tslint-config-prettier":"^1.18.0","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿# Perficient.CLE.NodeLogger\n\n\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@0.0.11-beta.1696","_nodeVersion":"10.16.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-pcsf3Tk8hI7LhE3unpSgZsFhdzComXhtj2VVpzW3OCQYvK5Hzh2hGa6nYheLLpJ0FO1trnEGsUw4rciH8JE4wQ==","shasum":"6378fcf7e94f64a7b6024889565b5cf49d376b5e","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.11-beta.1696.tgz","fileCount":14,"unpackedSize":29474,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdH3xmCRA9TVsSAnZWagAAq+AP/A7ckvwOSXzPIV2Nuxf6\nD8ccCyjlxfXX5qYKTIHG4of6pE2bUc35RbcPBaWwON9wwBTH0cajXXxVe8Ha\nk2jNOTwjPktt9gOki+2gX1rv4TbpvCp9bv3WQkRY2eMEq1qNVUo66qOptbj6\nkDnMj6l0kuOegw0ToNRJlBKX1OGKRbH/O8sDX/+m2SIimeOkmqp1en+5Ij3/\ntoEBdDytJNk99b9iZkK6tYN7ytVmtjAe96bPFCD0vXwK9+4I/DZMu99CJRnf\nFlzs374xnwIUKS1l269jtHIAMrDNqG89OPfcZp4q2hD+f2Vy2E2gMyem6k38\n5dsAfOWXsgBNGyyu6mAf3e/945S4QY7YJWgKDbp/opIRBmhsv8nW1Umr/rkL\nBp61eZvg0e37jTWU6CYroDuURlXEpsTLuHGqHllff/DXDq6ulDCFVwpT2sqW\n7dGZld/++t2gsMS9ca111B5PUTk5xDQ49JwlUvlTA2v/Uz302ba+j28yvH5x\nW1nfMSN2szxiwZXeODXFkh/GbWsaXtH82adfKogMQLHX/DunDXWJKNjwlj4c\nEjz7yRttxgVuasZggXlA/5nez9fht6bFiRVn/mS4SBcEoiwQWtUVmL4m+poW\nNKn5FWm4B65BtcVA7H2xPl3fiFovfic71rm4xSgpA3saQmHov9wMr9qJsM6y\nCKqL\r\n=TSdw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHenS18eGuio7iH1Mh4bCOHhFQ7LxXWz1EkK83ENiXwkAiAAnCxmjSo34lPYdVhtBalEmyZBn8SxV34sYPImV0bRVQ=="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.11-beta.1696_1562344550315_0.4116119086637777"},"_hasShrinkwrap":false},"0.0.11":{"name":"@prftesp/cle-logger","version":"0.0.11","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","format-check":"prettier --check \"src/**/*.ts\" \"src/**/*.js\"","format":"prettier --write \"src/**/*.ts\" \"src/**/*.js\"","lint":"tslint -p tsconfig.json","ci-check":"npm run build && npm run format-check && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","tslint":"^5.18.0","tslint-config-prettier":"^1.18.0","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@0.0.11","_nodeVersion":"10.16.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-gUnws4xGrmtgApOokkkC1FgIOU31uQcjpyefDWytw/SCVOp2hHIeyDcpvkV090GhTU8Wyw9n7gS2b2hykKXh6g==","shasum":"bd345bd34ec297404ea7fa34bb1819e6cadc8350","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.11.tgz","fileCount":14,"unpackedSize":29464,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdH33lCRA9TVsSAnZWagAAEfUP/0admbJqmoogpLU4Is7X\n3xMaqJ29PXjgbr0GzokdjqPi54mmJp12cmnMeebNYbe/mVj4YlpNmuTaIgcC\nEDRjEkkCvUgByn8KBrj7qgTyl+3pMgmobnE8aioEf3T9tLxSm+/b/mIXYs+N\nFsYEDWHftzGxhqW3oEyRfKuhZwIFHfoK25s9N23lcdGodTLUsBgsIi5ADv+1\nnqWIhKOTd5XovoqhYB/eFWcI3Awvh5tyDiOeRg67RqDXJHW5x7RZOfl4Q9Mz\n8qrrqscbYX7e2lMIYgzL9hWWjsVNyCByoiNiiD4vFv7ka8RMl+R0AvpU2EZu\nSC1j8fXB3opjFzlx91oQekPw5e1RM/sCxJEdnnGWtQQGqQLWWIbCa1msCY0O\nwaHpopncK394mVOeTxXGVFArFVVN2+1aPsAs2o48/CTvVAPmn1fvJn9Cq2z3\nqZidGN/AfZ7Lsq42TMxZLcFmYjkl85qN0Be/GvSZS7lCy25U6Fl0YMzsgqcP\ne+5y1hsqjLhhC2xHFXeA65ZyiMf5KF8yhgAVxf3ai8eAF8zX6aM0eoR4XfLf\nk28+TstwadefcpDlvnS5HWR51niOaFXQFWrP1+ldfs02QRd12yOJadLYSRYH\nAiwbwJxtakXpBSNyUxL+/TudXA4CAYtZ6I0zNm+Al24bOYEAtWFS1PRfzCDd\n4rWJ\r\n=4iVq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDZP0FoeXz7nCgOuHZ+w4XP0Oq40yt7L5oDFm+2iTIS3gIhANbb+EqvrwBemzsB5KHjLW7TFsUt6bf8X1rCQruXCvbE"}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.11_1562344933277_0.9161151499571936"},"_hasShrinkwrap":false},"0.0.12-beta.20190705.4":{"name":"@prftesp/cle-logger","version":"0.0.12-beta.20190705.4","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","format-check":"prettier --check \"src/**/*.ts\" \"src/**/*.js\"","format":"prettier --write \"src/**/*.ts\" \"src/**/*.js\"","lint":"tslint -p tsconfig.json","ci-check":"npm run build && npm run format-check && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","tslint":"^5.18.0","tslint-config-prettier":"^1.18.0","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿# Perficient.CLE.NodeLogger\n\n\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@0.0.12-beta.20190705.4","_nodeVersion":"10.16.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-mwx4yb8meNHJKFzhQV5uFlisWdhsYvAzL2h+1wnnTXVHsdvH3VXaCI1QpdwmP77MnEDj1dQfk6Ojg3t4eDh9vA==","shasum":"ebf8e69c23577cc4c81e4d95ec0fb145f59c4ad2","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.12-beta.20190705.4.tgz","fileCount":14,"unpackedSize":29480,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdH5A9CRA9TVsSAnZWagAA54MP/1JNC4//kHYul5/JPc4k\ncbYt/NueULD5vVRk2X8G2TAlhcHtpRinb1T5sOTMpOmwAu74V/228k0VvoYA\nB0oF/3vifORjUUUwK8Et0y1yzWap1KUKOFSqCJR4zmwBQzFWl7ogyHY01jgi\nOyxsF1XzBRZKoPeKY5fuFijztPpZfKyn2VtXamyuxe2d9Tcxkxwnwr9dPuYT\nCZnZ80V3wOMCS/eYJ3xhulHNdVrSEYcytcPwcBVYQd/97OZTksRm98D8kQOM\n5VUaq0iv1MSA1iIhRbDRPnmy5xbhXruHSyoWqmdwq4DdmEFjhh2/OE62yAfh\nHzGW3tvywYFMWLOXdN/QwmiEm7joIzt1E0z/v3COIMhHrCgbsL+Ry8H7aH9C\n3nVQVdIZa7hRPTQFWruTvg8zrMKcoMoscXwhpgQNmnr0dQFTckHfs6xqf2wv\nxch9vZT/OCAuMRYgaMXlSl5Nm95DotldGPh3PG1G/gjNIgjR/4K7ieMLPrXu\nL/tEAp6YT+GnDoMwmDuE8hi61w3XVGl82GXW0QF66PZf0gi64gilciMU7hvm\nrN9+4zzgWJBdTUZouJikF2HOp81kQN3gDNUvw0mTycLyGXzVFOAENYUdjNK1\nWJ5K0Y6FtBsLPW5HTlCRFsZz3NtuCQpFUj1g7eAGXcZtkw3qRmERgXZH83qo\nETci\r\n=3qMP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDKhycC06gjxvmyUsEyxoxSVrVNjQ4/NhoMdNyxA7mPqgIhANCXvVLU51QzyBkXQKkKvVmveHMuniwHWbVSwENQQL8R"}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.12-beta.20190705.4_1562349628423_0.48394615990113543"},"_hasShrinkwrap":false},"0.0.12":{"name":"@prftesp/cle-logger","version":"0.0.12","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","format-check":"prettier --check \"src/**/*.ts\" \"src/**/*.js\"","format":"prettier --write \"src/**/*.ts\" \"src/**/*.js\"","lint":"tslint -p tsconfig.json","ci-check":"npm run build && npm run format-check && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","tslint":"^5.18.0","tslint-config-prettier":"^1.18.0","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@0.0.12","_nodeVersion":"10.16.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-PDgR0SCBtXQQ6LiA2dnvMqsGKP4WyH1zxctJnDVwqi/76tT+Q0d7dmRT3HO9gqYGv2dz5vLVeL//tup0FnKwNA==","shasum":"c3bfc8204e0c9ea2087aa7e103d5d5eb2dc0f87d","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.12.tgz","fileCount":14,"unpackedSize":29464,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdH5DBCRA9TVsSAnZWagAAgbAP/iKQIJ6TE557s7JPBh30\nzIc/I7nbJHfuorSNm5ORIw8p2c9SY1oVyHTM2EsXkjs75O5RO5ADi1hjOWTh\n3vdUBekvwGMd3wk+oMNzQ5qrLlx64WmLTqfuJIq7XjFoWiATtE3dQYamPH2J\nN0mVdsMQ53nBc8qgEaI2kfsxXppJg+LhfjLIJ96VAxohydxeHoaIsLJU3crn\n3/5S5kanUH9mUIb25rCXHHx4J0KBmn7+TGlaMO1u61pevOr0LGLq3ZmOTb25\nYmHeeyk9YopGBVQ9ciPlj3NgLE+9Yi5fYLvjXidrNXPIiEAVWn8UfsQl+4tv\nt5rXIs9zagMa509SpOIaYMv00OnmafSIwlSsC4Hn/4ncO/Hsa+9/C5y2UJmM\nkarYudiolHQAzoQ/upvhuOvyvEgtYMIP2QXXpLLDYrZZYAfTIkgNPfxKvR4+\nAKrQrSUPbL3897WKI1s3IlaC2GJ90Vq19ujgOCgGozXxS1XFk9Flrf160qlq\nYw3VJBgmxLPT32IIi3fwNQMwl0TRFesRu0JTn8q585CRPoPGBef3VbWcLo34\nXKoiD1aI7YFqW82RB5ueNFLAVwXZw5jnOutdlGUammFcd9YK72HbKiwg4k/C\ngwXE5gbdpIAjRg+PbvSS6IN8D6sWr1eMVwj4D6deNI42Eas90f+jT1Vlnar9\n6TyL\r\n=3jBC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAs3qCC2aLAYqnjxJ2NQNAIrDlhxDX/CBi4pFdXE8BAyAiA6quYox+1dErIQnmcR56vIIeLDfNqoEBqDK1vqoR9SoQ=="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.12_1562349760507_0.6789565055627771"},"_hasShrinkwrap":false},"0.0.13-beta.1":{"name":"@prftesp/cle-logger","version":"0.0.13-beta.1","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","format-check":"prettier --check \"src/**/*.ts\" \"src/**/*.js\"","format":"prettier --write \"src/**/*.ts\" \"src/**/*.js\"","lint":"tslint -p tsconfig.json","ci-check":"npm run build && npm run format-check && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","tslint":"^5.18.0","tslint-config-prettier":"^1.18.0","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿# Perficient.CLE.NodeLogger\r\n\r\n\r\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@0.0.13-beta.1","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-oFeSG5rZMBuaZ6CufFcVswCO8UoZwjUnV2ENXUN+DFFqs+kYiL8BR+lXyOII3oS38HlaZ9mmwnwzzCDDnnoFiQ==","shasum":"8bb45bd726f2a7573743fcf64917ed4325fd714c","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.13-beta.1.tgz","fileCount":17,"unpackedSize":30320,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdI0UxCRA9TVsSAnZWagAAXQEQAI0TrSv36cdsH6+CN6wQ\n0dplp0dG7mLclbM+oDBrYmPJzI7CdEkEw2OXk7Vq77Qw3nFL+3lC1zotOhJh\n8pzMYP2S5qjJdoJg67v18NhYj+VcwqafepUl9ltkouGD3PGbworci69eU8fP\nD5J+nCvFwZBMR16W8EbMA7ujv/jKiN2CGnkm5vNmK3jKw4jv9JzQGk+LTlW+\nR4n5xutxed8aJZCyeRn9jpQ2a6Mf6aMvTA0UPT8c7db7njTjyfTwe9zMoIgl\nxFyoeDWIYCTJEFzZtjQNBjz6mbUDWBaBTLMIbO+vy1corywaiLZH9Yvw20Uv\neEv0y5L8yBA4Ph53T9d1Dcru09ROQMTd3PU/hHdFq0oQiJ/u7V8q4Vq35/+T\n9VF4fiKX8XdWQzScO4aYcL2NdK0uK1DBjIbFc/xsDL10j/pIxI7C/ThzB/NV\nxFOXvpaVLWH0KGbb+8UQJGJYzMV3MwwM8nz0PgclGs4Z6tRl+CeGzcVn8ndL\nINWARRTRV5AsZPnqUwTDF9rcCM+TAWVpQKLa/rygx4Wr8OBh87XKcoQiSJY4\nK9KOwNycrj2zEiMPeYWhZEAIiB6eV9/2Mmi/AtRdb4Hia+ICe5Hbap0vwUFW\nw86Gnw1GXigJbTb0nXTxgZOmG4YzSTamW1p/GhmTUd01oVqcEumdCcJGhIC0\nL7+o\r\n=QAJO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICnyzN1MTsUFO3DFKzyag49udMcxywTHQCGZBoexNYpLAiEAqQo+iogQ3JDnJNmy2T1xUZFmGLTY79nJuNqD9/qTJSM="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.13-beta.1_1562592560704_0.8663218741727476"},"_hasShrinkwrap":false},"0.0.13":{"name":"@prftesp/cle-logger","version":"0.0.13","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@0.0.13","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-2qTtKRTnMh7T0Ua4gijgHRooYQL2vL9C1plExtARt0JILZ8U4LGOUZPX8Kw0SvVVaj+RmrFsdtB5sOgB9nGadA==","shasum":"2708b25f885fd8b21ee67c77b2e8dda2495548e2","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.13.tgz","fileCount":17,"unpackedSize":31721,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJQQLCRA9TVsSAnZWagAAxKYP/RpNgFRu6pK8z0xm9SA3\nchR4GDlLDEGKbJ019UJDejoDkdMAMKJSNrX92bMDP1yQ00RmTXIp8OHsdjyA\nQmCWfKUZgqDWUTRk1crrOrDhpGwy1gMv8JbeJej9Y9qcWNAOBrAjTKDLyair\n0DqgqlNvicPI5bem7IIHc3m1eQeEVYH4rqGiwVLgs5fvsgWADLmdCDWjNDCk\nV1sTT3ACuOR4PyiRyddx3tL/owYy/dWTAUcP7K4ahk4Rq5r/DoTN5xJI01SX\nvNZSMB4Bnjg7yTqbwMH3pLYijSS3xHuT2FSBZWW6QA2EJCRD/lSAQntVtdbB\nJPkvv36S4gDd0YVvYhvBVuWu3HFi5UKqbRv1mpGHRRVHQvHTgtMYs6SqPO9T\nF6IIrnaFOurE4/B2uF9Uy/q/M2V7PmRV/RRO/bV1LBTVfu2Dd3H4YIvJo9GS\nDHPgGHF0ciecM7FB3UMJITPjr01LqS+UCHrJM4U1OeGjOmxwcMNIlrgIZ1/s\n9tlr4Oc3PsBkfap+JjRmn4598FJDIeaKImorA9d5f2Xnt9mdbwNtkTxCNYqy\nk+ibBJB31NuhpgFZM6U++6HH0eJZRekqPv6OJxuTlMEeIqzruAT4t1WDzCKZ\nclhG5Lxh0fXj38yqlkSaZmS2znxeCuZL4/R9B3YQl1MDwuU8Fxli3Qb+prEX\nxRff\r\n=qmuO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD1IQ9toctFVxbj6TlHdzZ5Zczuc3xEhTHGLQTGYqwBeQIgMucWunLEB7fo6mbn/FK9vwEYYXlpFMR293s0yy/btmI="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.13_1562706954388_0.7124417018524456"},"_hasShrinkwrap":false},"0.0.14":{"name":"@prftesp/cle-logger","version":"0.0.14","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@0.0.14","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-QIDCk8kfkyeRX6hOqXJaTU+JBu7J6JK9sCI2OQD4l6sYZIPs0xLCh0VZRiSAqoDTp3AqA2JKLXa9yKz6bMsvoA==","shasum":"7f5d0f04aca2453dfec1137d0ceff6c25dea73d4","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.14.tgz","fileCount":17,"unpackedSize":31721,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJShgCRA9TVsSAnZWagAA6B4P/3QJ5TNP/GBO3d2alynN\nJc6IuvX9ZFtNEJr+mfPLLwnhtvkEYjuJAw222BkDINr+rUI+nmZcF/GCXn4Z\n/HPDTI8RwEUY2CdXqLfpOLkJRj5fehN8sa8AI7Mz3UIALzr5jdwQCbjrwGQq\n1W1XaIncEPkWVGsM47pOWAcayme1GXGOtGLeSYjBeWgfrVH3/C/vTQvWRe6d\nMhfrm7G2wlM1smjfsD2PuBEFC9xyYiUVexAcSaQs7M4NGL2xVqccykqzsk6J\n0zQ3hSpP7vvLZmGIpL6TB8jri0g2xZXoAiLF0bcFhUrHoEPFXsqAWNc7dm1L\naAelRT5bFva3JIDUVPdQ4a+WO4EQsqnfQy6hRh0INBBqD0okWUTHDJza+iAf\ns4KuOgF67lYxlmzZdSLTXkhWmZh2pS+02EFwdk/43eWYTgWcSIe57UOf1U/j\nvtycbRje5NsJyFtABYgkfRfXCjCNqRb8TWQfyEfcaWPLk3k7taqLrzXIZL7k\nzvrGp9b1pXna4cLoC4bt76AJJ0W7BIOlGU0lGnmWDxVqB7VGp67GfcEUpqUk\nrUYTvJUD9TThRkXQgyWuHCy5TI1G8DVG30ypnpB/2vaWSiE+CSvGN0qvGyKz\n0ybrVRWz1cF9pL5//ce/7cWbW02nw7TttJRKA5lXz/eP+u0CV9FpVPjInOs/\npNPM\r\n=9QhB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCAmm9oJ3CXTJ1pG8yyxsLpuBjKR/vGkSHWwaeBZAx14AIgQrK6zUX5go0YBXCJXQJY8+W3kIAH1ssutmX0CeLCE9c="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.14_1562716256300_0.7539999415917951"},"_hasShrinkwrap":false},"0.0.15":{"name":"@prftesp/cle-logger","version":"0.0.15","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@0.0.15","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-rsHXV5o3RLTCt43vMw93SU9gInbbHaPEVwgVKg5nLF5uVLs260yhxHpffpMJ3TyPl1uaeYghZfXcqumgFQIhqg==","shasum":"0a0ba69c99cab420fb5745e867a66e423f455f4c","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.15.tgz","fileCount":17,"unpackedSize":33507,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJStECRA9TVsSAnZWagAA4QQP/iMXwuFbYNqop+6MGtnU\nMR5lTvDWx3WlvxWXgB/8Ny+14f0pFq3ApSBbTAGwwEegPeHaZsq5tSlj+gkn\nATvscxseXTgKJOSqv76QeTB6/OFaNE280dX3646b3o5/WV3AiqJQtltMr0li\nAIzq78/JSWx1cBsuhGGrB+yx1qKmvAjihT8kjjDyzmPm53m+ElcESsafKKhJ\na2MhOg56m1ORHZqbSOvG+YVslx0ugwP6hkpmiAEeFb0zekeswM5o4OH9Sjl+\nbP0rqc3MbsAQCV52ppgeK+MRVj/ooJQQ+TMJ3Fjny4k2Ohe/Ij648deCD3VP\nsd0ker8dNNpmsecFeQFSwsJVwrtR0lC/MQRs2uBFAywslJqXAyaEo0k89jpY\nS1bNPz/AqkZVe18A1JNeuDLrmoMv9QHZIfHMI0/CzNBclIUreuNLGIsZuT3L\nFA9SpMySQTF8M1Dmm93kxBoVbo23JWUBQITm8iyQCMVI2oq6PgGdvAvFmfx3\nddMZeJp8GbgfuaI9XdmTygEnZPkwMhk9IvL6bWNMr5Zxh3QMOldTTwYu3V8W\ngFrurOlAub39dcDWTbpatk2UPwqBXsPZEpZsm0gVOLZpWqJuZHNXIOccuO77\n7HMZiZLsCVJxttnoU5+eJirQQjw7XnrHC0ky8hSJ0zV4LfaDzjfpGLR18i/A\nktvJ\r\n=AamL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCa9wysj96EDwrsZuTGZNQCN0XnFvSnQfs0+0cLglfb7wIhAPgFttNNZ64RNwyxr1UVxpVOUPhLAhpVNUqRx6ORPfJ/"}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.15_1562716995121_0.7751025036995047"},"_hasShrinkwrap":false},"0.0.16":{"name":"@prftesp/cle-logger","version":"0.0.16","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@0.0.16","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-wnqHppW5jg01OjtuczqrFzb1pAK0thtPgucunw/Nqx30QAiRGOcdW1onbtxq5QZ6GCUydyt54EoRenDliRpazg==","shasum":"4aa4c873425a31233497a6fd6d2b2a672074b3bb","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.16.tgz","fileCount":17,"unpackedSize":33894,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJis2CRA9TVsSAnZWagAAKogP/1F6AgubE2vbVLVx04FA\ndoD+b5BCvtoZmKaxofk1Lfx70UajhquglpIGeE7+ig3slB6yBKBcPOow1Gjt\n6tXu0IGTqKDqckkOi51tyOVGz0McJjHJRlYrzOE6JqwTavUqXVAyhcGz+NL6\naIRfy71D1FljJ1bhd1DqyRSfMFwoPbN9fHD425Oz4SQ/2wBGQQkVI8b5bANy\nEM4wRq3jx8YKpun4gNKE+IYaFpmNAEBZyR8OWHwU8UZAzadZootbRX7drwkN\nz5cTCFl+iQ28Z7GKGq8FSO1dg6su3aeg//EhNEgqkhRFmx/iXnl9VROmoaHA\nqJApFn5stb9NSfmc0HNUXT/G5nXHvQbpqhMhRqsg1O57RbL2gPUg+nPX+hud\nbighYBFLbNQDuxcRlRnk8u7PYRvHY2X2oBzvbcQCvAsWD2hCmMfbqk+ORpat\nSh3mQI6dYTdNr8XMNbUhO6OOEsT//j8XFfQA+wT9mjz8m4hcFMGcOo4UvlWu\nB7wHkGfbq9CmXX0//ekEmnxKKT6RqmssYT0RNLaxh33zTm3zmj8N2193Zuqi\nDb2gBJJpKwTCQrHplWckXucNM6GtuAhG5/aisUL0bidJSkpYbV92ejqSlSJM\neV+Vl+QbaCrbILfWj/ZfkeWRLiq7pAeLmCHMk/r/8cVcGVv0a5oSJ9koFwcu\nUUYq\r\n=TJ4f\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCXOMW+Ki1aO+lh0WuVc2uyPRyHR3hReiEGLc/I/64Z2gIhAIkzTWZFgrMR1yL0Qc22jhlHISJzjGpSUT0ieRLVWpjq"}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.16_1562782517781_0.14042767254344057"},"_hasShrinkwrap":false},"0.0.17":{"name":"@prftesp/cle-logger","version":"0.0.17","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@0.0.17","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-4FSEE7t8254Y1fCy0AXz7csFvtqe6kxaLWlt+E59Wf1QJHsHRgWCOulISBz4bQJ88hpHPV80gAMma2NYDlAK1A==","shasum":"8f527320c4035b6a88829d830cca58ddb755917a","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.17.tgz","fileCount":17,"unpackedSize":36215,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJo30CRA9TVsSAnZWagAA2igP+QFgaVrue21pxTs5woOg\ngZh+R11dcdmT0GZfXYPLuFywICFqLeUIi9MBpXklrBy81+OyxezKttH08bou\nhnjJt7v/JdZdZs4HjDybxEvlbtohe9uW4cAnAWjTEfyiBgM3K9bqiQ5VemAd\nuYjXberZL7oAf1ULo6hu8cjetZg69ZQFPvrmpt4JSJs3P9oOVW+AKHx7EOuc\nLQVx+4xWrGHzYY4yKuf9ryn42tTPKVNub1yPRD1O3pS+zLx9kFQD9uOl1rXh\n4yfUN/MhalWc75xQYkI7XHhL3IPzOEwz0uJf2bROPe/3xO+ENulOq6O6mIuW\n0fGy4fm49an0iRAlY6GlSRz2oj48QB+Yma7cj59ViqEpHqlx+I63o3PtaaoX\n9vtpoF5rRIvOzDS/bqNV4ZASBeGxNIiD+gxzWmdXJXmcxEl0pdFJCTp6EdBx\nKXzx4pPfLDMcBmQa8PCASGRlCS63SX+ZiS7vcZCj8anqFCuRwxa+BEaPEohm\nsdTllsSL7DnPRkVQqZfKCNbZWd7hnV03NqgC0eI2Jon7OWkWqW8N1L0ebLIA\nz/K1WgO/Bjn8JTOshKiVC1OUkOLqMLUy+fxho5nac/U1I8GgFtGLCXjYy4Dg\nv5Ov2vXE1oF+qUwRSDhEuJdo8wyF+U9tmVlaRXyOQ62dXAVcRriO5SRgiqbk\nx0gp\r\n=H8xj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICN3X2B9o1U9quu16v2ljHa6In5BObEVVxluTrlbWVcAAiAcCzms44JGeXi6NcIgBP4Xx9hZkFEJ0uEiB/bopqd43w=="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.17_1562807795908_0.044279185193859094"},"_hasShrinkwrap":false},"0.0.18":{"name":"@prftesp/cle-logger","version":"0.0.18","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@0.0.18","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-IltY9FMnw7YY18FybvwBmCslJobaQf5QbsQYf6iuHQxhseZw4sDS4JMLl7M/6XLYMF1QlIixE52nJglJpwxcRg==","shasum":"d9c853fcf7fa85aacd1e20b0e1cc216ac6596202","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.18.tgz","fileCount":17,"unpackedSize":36215,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJo/sCRA9TVsSAnZWagAA7i0P/0Hzh81y6nElRdaRtqfu\n78U46iBk3F0hjZqjG6P7JxLNtY78o5dPqaw7bpL/gej4dyqPrjU3Rz4rGcwD\nA3RNcHILEfb9VOK7mje3vhYhiin3S7q5y7lHxStl5H1oYof76MJDQMwnjLC5\n61hQYVpiZ16lrMP7wVQKwrzBHG+jPi8Wj6qgmliiGKbpHZyvbgvu1qJguimo\n4XlVwnEJyfZQis/5f4/vQewZ0EnrZsvtCBRjunYadxTVfAGgogULuL6e4ICB\n53DpQPV3bhF0SVMDfM38ZZGufYvHdy7BqFdi4dUm1U9gI999T0u7+HAptVCv\nFACP29KLn7/IlDWhcSZzoSYWKnnFVvSfIoRE07BA2M0nGgMacQaVMry4pWkk\nEEupZb5hZYQZyZQIpYTwuh7maj8ygUwjBzDw999P3n6H0tgI4jtBGjJUJ/z1\nwuA3gNHmikT/Ts43OxKUbRLwv7k3ewy0dOfIDSE1foUOzNuBPH87aCw2nXoP\ncD45tR7VLerUyf7OB215ZqtXrbSgzP5VpzgLR10EXqlk0W52/I0Wfk8TEn8k\nOMNLSzcdS2yjWon6ZwFA5IwJ0evJcsYCMpYj04PfnOptEKIaLYe1p01N4bmK\nZjrTB1DyrC/nW5TApz2vMY01r5rjdb/QFBFvzGh9QCFYyDx7+FOSC6+rSTQQ\nk0nX\r\n=5SJQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEqq7Uv+1mWmvehdUj1bzwZL2GNtxtJwN7Vj8c/XMtnfAiEA1P8nVBLXcpHgImdfAnLKsfks955tql1e7coke6Xa6jw="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.18_1562808299698_0.2310883164045101"},"_hasShrinkwrap":false},"0.0.18-beta.1":{"name":"@prftesp/cle-logger","version":"0.0.18-beta.1","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@0.0.18-beta.1","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-R3Pv8FlgewL/yjI3mEEYFLx2RJqyeLTMeYkB1C4aAT2hsPJa9au+SyxTmbpOUsJmY8LQ7Up5/x7PtfEMRyrA1Q==","shasum":"b09ee78d12cd03003d373608cf884a876d0ef0d9","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.18-beta.1.tgz","fileCount":17,"unpackedSize":36222,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJpAyCRA9TVsSAnZWagAA+UQP/04AYRhDGidt+zbfLA4e\n9J9gZDm1pGlbixvdMVBJiwuLibIKws1gS9MBmI02EyoaoId0Kq32Ymgsi5TO\nrravqrHpsmW4lEXdJknkuNfMr/G/oU5apc+1OVCc/d3UFyyn8yENfwfw2EOQ\ntvbGwlHTkOfm7WZbLJu6vloaxkznhtlA8caFxPtBMXjf4gtpbawYe1h5Dy5y\nEQGabXQWc2mG0057D5At5TDnYbNRc7arXhej+OrQHxHO3xH4quSclvlMphV3\nJNouFmBhe+DGr/9g9BWiqW/59OdP8sqrrwZWZgLMLO1rhJSDhW9+JFOvv95L\nV0r8Q5j9GLm+4rv0hDv7pferqO+vpKWIwYO6hFkINFryD/J8JGlvINYsvrf1\neU4SscMn7ogqODWV9SFTBYM+WR4lEHDt7+/JUj4q98fufKhN2ApGdQFY4sYU\nfmmYkRE0aWIRrp9AP42jZH6m6kDbnZ1kevt7gHsxG07GOUVavGn/+AJe2BF4\np++mHXubz37YSr5aoChmDU7ZKst7zXwEu+U8Ivjz1UIkzxiFXRB16KfswsJ/\nRE0/dxqGss3z5bAZApooKOt25IpPHtP54aFcAE+5wrdg1cw7Sq4Q6PnyQwsy\nNXFadDJrbuS0k+h3VYtSz38V1v1SAB/YscRIQkop/9exLtttkNvgPR3MJcD1\nf1t3\r\n=s8T2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCdLb8lgHhZxE5W0s+4uGH3d2mtDOj0Uhxhk0p9aQrP2AIhAMBt71D/ygQpdYVv3m0imZ+EA1xa3PoeH8rDMiDh10i5"}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.18-beta.1_1562808369740_0.6108171324454019"},"_hasShrinkwrap":false},"0.0.18-beta.2":{"name":"@prftesp/cle-logger","version":"0.0.18-beta.2","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@0.0.18-beta.2","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-rtJ2XTjwcqKgdiYcwX4lsTE4d260m+7u0ObxTF+1kVoh/TMRobxmn6YNLEKgLI+/WeTlE+Tb6PcyNGpP6JXLTg==","shasum":"6cc5b4675a5aa6fada74ad7e8b1ef00a9c40e657","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.18-beta.2.tgz","fileCount":17,"unpackedSize":36222,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJpDrCRA9TVsSAnZWagAArs4P/iVTK5dtGmq4NDV6RSk+\ntuksOvu+FLZBoT1KCk9bFHeYMKUInM0dOxTL8Nzre8Wdgmlg96FdTssSDhqP\nXbK3nSVwYAYLZqhqwyDTd92X1v0EcuTqHJcA7WK2sf9d2mGFry3IIp0IcsoF\n+ItS16XY91cj0Gt8Ho2uS7oL8iGJxQdDNeu0XHwtO4/29bUbsu1kDxm10zv1\nwVwu0FV3UV7hZXfk8+j17f/cQITs4QDNsgnaEFj53L9UREsrruD4p2NGFtV9\nFFgvoUIdn22oVczHYV2mtU+NR/UD9pwu1t397aWWih7lrpfvGtTykIfpnHsd\npDOon4KhwEb2Rs0eda+Y1YhBxqE5TBgtJ41BiB+aMN/E/axpshg6CqDmrjmn\nzRLZwyE7BfF7tutwuQbsItL94hQ4VG+OLi1sTCx7/6mrfu2a0nREX+OdyTql\nbaDrODp+zoMDxZ6mD4irTpCXPnLwW8D/t51Gra9YPMjPaeM2j/rxOejCxJIt\ndr42yb+irjdIxFAhyM7cfEN2wLg5L6+p4tprJSAY2mKz10D0pwBy8BU+xS7k\nff+dDiqSL8pcX/la6pVEYgyqCqH8H5Juo9jQ69v5LYFPNk7FpyxCJ1IQjGQv\nH0haKcvXkI6txabewwAMXLfBabV+Vbi3/GU7z73YLZDJB6WbtZ/FSOa1wwzd\n/PPF\r\n=Rzoa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEkJ6wwNhoBNiE2SRbsoB4OmnmT6kZgLnbwS9NwGzkHgAiEA/BTlcJzMOKggCdhAv79g+gOQrQzG24FlGhxwFdkMOC0="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.18-beta.2_1562808555306_0.9426828215110679"},"_hasShrinkwrap":false},"0.0.18-beta.3":{"name":"@prftesp/cle-logger","version":"0.0.18-beta.3","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@0.0.18-beta.3","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-jIywiTnq4ul4VSuPPnhY5OeIse3t962iuEUtXPs27L3+Ab/g6FJCeeVHQtGVyFe/t7j6VN3/46y05ZnrOKNPCQ==","shasum":"21bc107fe9df6a4dbb9e8531d7988ca4f733d4c7","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.18-beta.3.tgz","fileCount":17,"unpackedSize":36222,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJpEaCRA9TVsSAnZWagAA4AIQAJbYw99l2Vz+qsU7t/jT\ndYDMsdu1NRkH8WLtZwqNMRUpLu9gOE6rsFIi8194WNUAsy7aA1Qy2zqlqKPR\ndM+TdKYMqxIT05Kef5XZ4QsTW926BpStTme1t/ZX0CIGN09vPXEZO7sdvcU3\nvjzcWRMa1WYVjIRE3BGukX3LIU3CVJ3bqRK6CjEgaVWZfd/HLBZWKJQwNgL+\nBMIgE0pbuCHpMSdhVNwBDFzuXI9VglH3UCAJKl3W85+QlF+zpABG26sH+i22\nzWbyAMvIPmU+Vh1+QIsEEvFJ/ihxoPwoSoBVHGDUFrT3CUc79/75F31kEwyN\nxnP8LB8Bu/iJbCbj4+ZtSwe3UTyqJx9+nOpkyoX7v+2Lgk81ohnyEJRJuI1j\nd1bc5vhAcFVt4kJmkWmc7Zy3xGknMD6oA+ig253ip693ct7+9ZCfv6EhgPvK\nx6GtJVF+up4guRadndSbkDGiRWCwxHw82hWspAvF0MjfN8LmEZFg4Cu0V8ai\nFM70TYV6JBM7L/Y/NGPtDpeiVcJPT7Cp9NM9IkYrpBuZ4cfI+JR4huSzzcHG\ntrDlmHk37G/Jb1r8ktTula8ZdDIuSK2fdfYzARTXcKeIuG2q4qzGqgUesWv1\nIAK1Exlk0TqXjjSAkgyBzY3oGlMkgb9GK1jfJ2HickgiRjKU037sfezzBSQH\nuvOd\r\n=b86Q\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBpP5r4C/JAD4jAoTpJ3ksQwExk9WefWvXvYFsewQ6T/AiEA6gwut55ATufhxchU0Pp2+rWtvTt5YDR6t0P/x3vvh70="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.18-beta.3_1562808601936_0.6772554690893666"},"_hasShrinkwrap":false},"0.0.18-beta.4":{"name":"@prftesp/cle-logger","version":"0.0.18-beta.4","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@0.0.18-beta.4","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-ARVQIDlx0SdgTCSt/QDhH1LbSlkyUI9n3Dlykp6KT+JkVLef1gp0vyvy5RCb5yJuPE8p88IZag6pI8VVmLs6pQ==","shasum":"5e9ecd3f216b6b1ebb3f24ea747109988e580c84","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.18-beta.4.tgz","fileCount":17,"unpackedSize":36224,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJpM2CRA9TVsSAnZWagAAIQ4P/3xBURSCyeashiv3IWTt\nq+lInKz/B/cqQpCHrWXJ03/UtXLA/kTw/uxGxKFfrZEELxbILk+sdUe/7U5j\nOOW1Okvs/1S2VbwmFb6VrPM4hgY/hs8dW8cgZsvcpUqKMakgUxFXBn22hc7e\nKmQghzBk37IpZMLm6xKlz1F5F2zLv5ZMmt922zpqoEavTpHFDBnwiBFzn2YL\nzSHhFfcfEzZKUXmEgs/3pqSVEW3bwEtCjxlIHSGZx6dki4EbtF8Y0cvW++UK\nOPwU5mVo1RBcJ64CPD7chIchezttvjh00tOMl+4qA7XluLCWZThmzuNDujjh\n6wV3k2EcyBtw0RaCH21XHcxoHnXZymalS5cgHw3aZjmUi+OtzA54HWmYKn/8\nKLn0TTCQOWkEKk4qEgN8+5PQWo3S4Bn7fEaLFC34qOArJU+4kpt3FFZ0pMNH\ndIK41wZZWFikv7Vjr/x5HWxoBkFiUIe5H4Dg0nsrv7AMBGtxzZwaNpSm+7sO\npqqu5fvgzBUvJB5u4O8Eq0mSZamkxCiwkPlWNQd9pOxPu/02PB/gaVkIrkBt\nUj7UDSiOpKC2p13Cfy6Wm5by/1G+LAfrCG1kZpdwgI/bn8oEdui93qL9ZUzj\n3SQB4nBX+berSbujRir7G/Atvu79KwmWtE3SPpGhps4qBO+SpDItNOTWepO1\ntT47\r\n=vrJV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDOWdQaGcfvdE0M0NR5bm7RCijpwxXuGCqKbjd5cSLuEAiBLVuiiD2ZNkF7LwsuQZ19nLUTCT9AaVTj4mz8aoVctZQ=="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.18-beta.4_1562809141652_0.7351985765177462"},"_hasShrinkwrap":false},"0.0.18-beta.5":{"name":"@prftesp/cle-logger","version":"0.0.18-beta.5","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@0.0.18-beta.5","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-999zB7o3FUKFp8PoXo5MekbgUDxZs8zw8ffG47JqoX3crTY7+ClgRvrn8V3mUYMUIx8+j9YRd8aZqhKX5coeow==","shasum":"1460d8b73075d612879f21370c2dd843daadca02","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.18-beta.5.tgz","fileCount":17,"unpackedSize":36227,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJpO5CRA9TVsSAnZWagAAaJkQAIkXuq6FXO+U84XgcgKm\nIrEum5mklcPCjxMjqK8HkcAqlWxykNPk9JaTtRUIUO9NvXtkGfx6eQR/Jwoc\nUhN08v93VAzt1rv6Xaa36GZuWWt9EFzEVJCPO2CegfyDm9yphqR6nZiOXh8c\nwYcDHpcn0tSUJSv3tWqHM5s2T+VVP2WzJSWjbFRPrGs07/suYYXi81V/RaGx\nlONihcTuPCzTEynRk6UCWISp/A8CW/a8G9wecPxfhX/TaT2EgqFUsn3V6tW/\n8kLx63+53BKb384WBC66mF42m46wDVzKO7Pe2hVPCJGq62KyKAWC6SmC1jvh\n/UaL8HzpVwr4Bd828wXVyGZ1ZX+68SO88yKM87riyMjPW8JSuJXeLsjA4pe5\nvWsaiiYGIm4tcOY4EyUDhGou4LYXGkt6zGzG/0WnKiKwlnROt6zgad35o0O8\npY+ZLd0dcEJ6XtS02MFZuvY0QI9H+x4B7AqgLVm9I9KMliEAPCsxskHwHvIZ\nXxxffBqQKrpAAbl/Z0cQBQyJbz9HpVSL/fJV/S/4KP3dhhm6s3T/+eDLrb7c\ntXc/IfPzfuGYW2ytnvIED05Ej21ZOCkN323k+mz4qCprY5yRQD1LlCoHMLBe\n/fQNmr9wLd8apg/H/glPg3rq7ya74NOgAZLEoRphbWEaupqQJ1i6faqQjfa+\n9+uE\r\n=6xsr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCsQqQ/IDFb+fEmvfcLqj7DhTMcknsHkTW4C+PhfS+x0wIgIpUbUvsh3bTb5XdjAjDePcJ4ADulWKN/j7ObBJqMbOs="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.18-beta.5_1562809272795_0.15950360056346025"},"_hasShrinkwrap":false},"0.0.18-beta.6":{"name":"@prftesp/cle-logger","version":"0.0.18-beta.6","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@0.0.18-beta.6","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-nObom9idRb79m39gJ7WqKTAdy5cEr6PkIR22w11IteaFqB/2GQz/NGI059v2qM86TMIpXgX4msk4flFlIN3OLA==","shasum":"f2501d9464a17f397e708943c9c1baa43558efe4","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.18-beta.6.tgz","fileCount":17,"unpackedSize":36225,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJpQFCRA9TVsSAnZWagAAE+0P/ioGpgN0GtqmLe4pbDAR\nxaW1KF9aTi2lw+m9E5NozCziSLcyrbPVQZaKXd59Ie3VxWMwm9TXShtVev0f\nVfxd6SqdIevmXIDdTmq8hIgRgDNWhu6fHdaTrmSKMhxV1UsMlu2L6IPSnU0r\nCUl4ytaBDDrc3iQAkih2hk1ZocAFVgSzjlbmWVK9Q+ybFe+LvJOUNcWrpHZh\nW0JHz0oM/0lOEza2rGMIpadzZJNhvudwqqEPEjN0f4F19Mf03RG+aFt02yyE\nW55Ttlk/rlPyfNdvrExEgeEcvvMsUb1PlFLP/Hkg/U/LS2hSK1uAlK1IAizy\nt2JacmaJRfNP0XHBs8tp/VpVLSOdDa7PW6a5wxaNHpwlw/xVoT8Xg0W3vcwl\nLa1B5a64+AJRV4sknMKyWN5Pv/3mt8MFgvj1SQgFsJB0fh9D1gs0K2xn2PGA\njytbC0iFqM2+uNQDhORfiLURJMkejZrsqjFuaDSy7pQmwFKTTDhlvgEUMzeA\nwO69iK4MeUTOGwgY000iIDCbQpNj7lHPIDTVSJCRAAX8dGFDwv8+rVv7mZT/\nhIG9luGnZ5NSg//L1/DNiINSJGznsvqgHroKBkVovDpFulKp6gWoDuZUIeTp\n4MnIf0WnPLoVUK8kkZRaD0OlOuYxlMcDvPOXGQ5utq2tUHErPei9/SVB5bd4\nLM95\r\n=CLE+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCe2KjbhykmaFJdAkHazLrnA5I2i2QsDNDEhZCsR0tabwIgFzutQI6Gqi86HQ9m8fX9AxP9WavDpgGzcMeGC8xG7cU="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.18-beta.6_1562809348711_0.4020266900648939"},"_hasShrinkwrap":false},"0.0.18-beta.7":{"name":"@prftesp/cle-logger","version":"0.0.18-beta.7","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@0.0.18-beta.7","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-zNDjyPTiI2MF2lFCrDmOT7ZHpvD8+iVobSythXrvRf12a9iQz/8I+dlCvXneoWLTJ6bONvLRk8dz6+0MQ9nLqw==","shasum":"25ef1f36d0e41591679060728765469911d2abb9","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.18-beta.7.tgz","fileCount":17,"unpackedSize":36225,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJpQlCRA9TVsSAnZWagAAr/sP/jSK5DY01SOKuDWZKvWG\ncoUeYcdRAmy4YuU97F9uCQdhw/aAgoxKyaytkAX60SOlzR1+t2B/FxWdlB8e\nqu5YkU2prEclGNsTd0eZ5Xi19MsJ8SQDulbrNpnorMdLsn64T9/rpNUtH+SS\nyxLWTbH33Tb/h8fZF4nGtwgWh4rAM7djY6bgLOgpYKDOArNo4eofjcP5gCDV\nn/xXNSsG0tQES2Ab6r1XKjEhAi+2D1s/YrM+s9K27f0wmeDVmoB5AySr0iI+\nm4ZQFlBDDOeCruVAkJ7bM4lDXiNNXLA9Df9bDKiljvCVK6sqEwu2FHK1K4O4\n0ahIaqAr8UalRbjg0RCaKi2LmLTx8eqzO4QQw10foMVobbHlGmkTMVhzMQDk\nMGbDAu1LO5YcaRFBw+27HWITKbijwR0AsCcvO2b0ejp55o737UtxquM48KeU\nBsPjnHBbHryqmRHG9xdasZC5jt4unwoqSARgPq4O8AGyq0IkIYAa/m+BKj41\n6m89w4pcftj2z04sE+yqIIKvcTS5UQRvWTKBGJDp1KzzUacuyGxTQhsSQy+e\nJKlfStdUVkVLeAWAkZouBihm9ALtVi/TyTTY4Ztsb8juEHDwPF4MBokQa0WZ\nn8YlMJQDsDT0FE3DedbAzgYKahMoMfUVZN1yRVWE3Co4hAzoFFEMaZ0mp8Ic\nV+5u\r\n=uhSi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDPgu1tD/SnwQkZbxQqsF0F2Dk87aQePPx72k1dtZRhfgIhAPgB7HWx2t4RJ2LggPT0SfHaWxNZM42WYO0h3CTHRFJ2"}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.18-beta.7_1562809380393_0.9371153057262589"},"_hasShrinkwrap":false},"0.0.18-beta.8":{"name":"@prftesp/cle-logger","version":"0.0.18-beta.8","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@0.0.18-beta.8","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-mAzR/1NfFbNd11HUPH9KQlSyK3vVjVhKO3s/L4ZFCXO4uq4exIQTBkd0KkXP6FG6p087Zccm5ssSyWkXGvrzcA==","shasum":"c25af080ffc92f5fa0db40fed72d4652401e92d4","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.18-beta.8.tgz","fileCount":17,"unpackedSize":36222,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJpTTCRA9TVsSAnZWagAAv3IP/28yH1bqdObGiIcGrsfb\nQWVKJ6ntjjRmmuYCtaRynQHTa7ttwrHIOnwTufgTDwgi9YPsLGjY9qoaUvl9\nF8hyECFqdNRMChhC2Aex6He0lsKxOzZb8eYmOAXEQL/WYlP8QZ0G5lUCJ5Ej\nprZo+XkKypXfVBt8dFctE1AvqJGgFo9iP826AUwjozK/zGkxrvTtb9sz6lq0\nN4gUcb6BF31K5WR3YVrnC13iC0zXJIkd1eti8lOxI97Q16QZUW74BA/cMag3\nX5JHkYXml6woBHOJuOYQBzo4UIuddoAfqp03PQFu7eWPakjy+Is0ms5H2g4I\nkOo0+i36RBJlsyom3d2OshNclgoZiFtgcGHhPxBSlwJopo72uEBG6hWLA++1\nUBtjrR+5sQSxIMp4VffXAYMaLUNWwkaRU5WKDYhnoeHVb9PCnO3PNTC0f715\nxyJTyGUNFTtUZtOYM0l9xhCglu/3A3E4RpHkkc5ALauaCUoGwmqcqd0ioSTS\nFzfOXpXLuX6LyOI6f+FG9q6bjFdgb7iFBWp1sY9CXk3mE+BypgZ3Dq1DA03B\nbpde4MIdH14lBmtIhDDbtVj5oXedPpzP3QPxXqCLN9EUcj2ure8Vx078+hEG\n710nNkqDOuiRgOgsyoQgjfEwWurNpCYu/ECxzQTtByDr4CesRMx2/XWUXEiH\nncUl\r\n=Xww2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCbAn2KZxrwpw6z48N8l+Xuje79jYkpBA1oQOOY6hMWmAIhAJsAi5u9FO/6tOn2NEo4dNYzanQzRRiImSGKnSDKRasj"}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.18-beta.8_1562809554490_0.5968172808248089"},"_hasShrinkwrap":false},"0.0.19":{"name":"@prftesp/cle-logger","version":"0.0.19","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@0.0.19","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-Yu1BXo04P9knoSITkGDA3FoPkDc8MUpohpvRQHQhpyKO3sQ8z76HyRGBqjEVfp2fgh/sbUQNQXBnFRZR45Tiwg==","shasum":"d0cd6a6ef879a50228a9ed9b4f53a7ba3cc86451","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.19.tgz","fileCount":17,"unpackedSize":36218,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJpURCRA9TVsSAnZWagAA8JQP/3/Fe5+u71oowFhrVISE\nwI8vAxbaQEaRufAHLY3F12mRjE/Yo02Op4Z+tQemgnOjvKvf0sw/OaEmzWok\n3Nc7jGkRJO1b6ijiGc8SsjqXCRJdfrUBE/GN8wg3BCdXkdySmk+RQ2teycxt\nrTzU5Y/zEmEWHTQ4EoJQ1+enl6gdi/8xdum2u65DuwPVXBuqkjSW5Ll9c17M\nbCvFq/fTK3hSpDf/0Aa/gwS7DM/XonBQu3Xg1gZPHt0sN54qPAnGEksJUj5f\n+OfMtSXghL/mJlT90/dFEawmcxjoIt6y/bJKoW8kOdLc5PG52QxPAM2q7mF1\nKq/iD3trtT17ra8ywaZO7mLrsUnlBrGj1etKWDmqIPpxa95IjNr0dy7wzq8Z\ncKr4iYjwXLaqoY5rydbnpD38gD0xiSBfUK4jlMvKwFYbCYcE3YiMi5f8K2jI\nmFkH14DfHsdvWpS+JA/43UMHOOJkUfzOu640ul8Upbz9EB1hV9Hdk5k1GFhY\n+bd2s7CquBHSW0A5PEqorUMc7S9LRQDlFMRqu6efaf2n6kcSxjheu/D0fmos\ngmrfsxZJ/ruHvwNN8qprslCahkuDzkMH8I7DsXdVv3B5hQpv3Be5M0Po1GL+\nsT4BZBY5o8wyYuT5lLSvgXJ3SDDVXYsT8iStn6nz4+c4FiSVar1dU/NArQUo\nXYSi\r\n=vcTY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICzwJ8xAtA9vOQjufFXTCz+VIG0QsEji0aMK8f/Pn8TeAiEAltev5D1M6L7DpHbvPixb4SwFdgSNey46UQTL4RmTCUI="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.19_1562809616479_0.546558246889894"},"_hasShrinkwrap":false},"0.0.20":{"name":"@prftesp/cle-logger","version":"0.0.20","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@0.0.20","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-+NEVp4obICGv1MpQKp7GbPXDmhsjCK3/0wdwbObEdT6Ec4wBOmgSBh5HzCOhI0o5pJUDTmYt2aR2cNljqWFwcA==","shasum":"dd0d0f8d12bd3f0eb3543764cde6ac4b1c3f01fe","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.20.tgz","fileCount":17,"unpackedSize":36214,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJpWnCRA9TVsSAnZWagAAjuUQAJLEi3g00Zcp+KcJnk5j\nYzPLeXiibv6XeaMRdE1sRE1g8Qq+V+aok0ru5tS3tgrVz4p+J/8hIX4kwwP2\nXUZ+HHSLnMGQ6XVSrALV+KdTXnYAfGeW0g5SpOjdwexOW2Es4q56WyOuhc3F\n5/QAQP1ZpyyaHscE3mdUbs+3B2EAJO8KmcUkvw3jOWw2FEmGszbAP2Uijb2G\nUhMVuHfJaLJI8fiCTORLNyijUf4MQHmdNF9zPJE283TuMppwBJGuwBTf1OcP\n6b8lMYSOjcTPUGrPqieeV4N35iXoFicgNPta48wEPjHZ1mBePuzM8cyNDCt8\nQDOvJx6NwtIOJKvakLZxuTSKYBM9pX4stTbctLItz9BwH/AANGB66gPmNX6o\nMzXUghZy6/US/EuHiuiS2Vrfw0d9KdYpSoG462mbjeMlv3bvg+06cxKeP3NA\nvOAK2MtQ9E5fG2fvOW3GzT8Fj2SUba5px8rMVCmUvSIlNiGN1vhEVknWZlpd\n3iAU0qZXBT/dpvvUPusiQ78w99IfSUV6lbIccojogSWm1XZiXtpxJ6/J6JbG\n1RadZsDVxqssSpI0BQtK0tIiI4yJPHjCAZlgR0pAXKMVK09VyDbeM4cUvp9C\nVkWOU6e9N4b+0e//7qBpsYjdyHzRiXDGScI4/PGASX5V/mejEyWNBfNDhgWL\n6lGu\r\n=JlOw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFBNBqPZoRLzlzaB/CdOMO65Ew9s75LwnqGSgLgJGMXqAiEAufs1M5YxfbYYQtqbE0tkWsEShKc78l9Zg+X9PYpuirg="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.20_1562809767207_0.8894121021128969"},"_hasShrinkwrap":false},"0.0.21":{"name":"@prftesp/cle-logger","version":"0.0.21","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@0.0.21","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-/eN0WL20z36pM36AEWgEkGX2lAC3KDRM5b0A1yHjRC45KvEjOlsAF9NGI3WqQjoCG9jkirArpSKZAm3ERFEDhw==","shasum":"0cf1b938eef4c636112f1c6e22316052452a17cd","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.21.tgz","fileCount":17,"unpackedSize":42223,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJ1wOCRA9TVsSAnZWagAAXD0P+wTPkbmmsNVNsg1dTsDl\nOohLrpd+KgwutLnW80/c0jy6/mg6rIHnbzv2axsUPEGLSkq1u6v7yrM8eQsM\nSGwggi8PIHa92T/e2te0fgxjI5Y+7Im+vxI7Ze1TjExMPJxu+Pip2pJRlcYH\nvoizaI29REJgfhbM7PLUHOzLgCnYi2qdiceDCkH8damyMsgrcynTZ5xMB10K\n0py0/eY2wMVFRET9oQ1QYwDHZTXbFmQZsIqsEYX9daxwxBAFfhlhmv3GJh8Z\nOQnvu/pLXQ0XjpsWZkNzZz5TG5kzbWswjadLC9QK4sK+PjnFTUuxE+iTJ00J\ne2savHrVkMLpAm1KIJuiZGfZGmeKoJ3MXbRqTyxfc9rQjJyYeDR+L3OoqF9d\nzH/glbiyBxDGDlq7N8wnblbzywp9UBkbMxUtx3k3FBm5XVSVxzSL8KfdAQuK\n1VGpauKP8CM/BBlex0/JU2plQcjsCi2sOp3/bGS9b1xh8Nn29tBjTEZIyl36\nPyOiD8BqJAueC2C0U8UySpjiGUUPokuiheeGhqRxgl2V4+1TFqBDutmvvMwT\nU/fwXU5m2KSnlkTXbgF5Ld8NmjwztPMqQOA9WK2+iq2GfQZBz9ZO9mq2dwca\ng66w8ZQ7Y+zL9c6L3fepXHWt/WvrdB0jcB6AiiAda9R5zK4I7k/7Py40jY1m\n3bgc\r\n=VT8/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFkjqBZ6NNFYk2fBFfBARXaj71yrSaB6ypcgUb0ZMKVrAiB2dMkXrMwlcRpCs9X3SeGqjfGKskEjWPOMiaMSw323Gg=="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.21_1562860557956_0.7024527253033535"},"_hasShrinkwrap":false},"0.0.22-beta.1":{"name":"@prftesp/cle-logger","version":"0.0.22-beta.1","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿CLE Logger\r\n===\r\n\r\nA configurable, isomorphic javascript remote logging library, with built-in support for Twilio Flex UI.\r\n\r\n## Documentation\r\n\r\n- [Simple Example](#simple-example)\r\n- [Installation](#installation)\r\n- [Features](#features)\r\n- [Configuration](#configuration)\r\n- [API](#api)\r\n\t- [Logging levels](#logging-levels)\r\n\t- [Logging parameters](#logging-parameters)\r\n\t- [Available component names](#available-component-names)\r\n\t- [Available custom property keys](#available-custom-property-keys)\r\n\t- [Registering Twilio Actions](#registering-twilio-actions)\r\n- [Changelog](#changelog)\r\n\r\n## Simple Example\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";;\r\n\r\n// will log message to console\r\ncleLogger.info(\"log message\");\r\n```\r\n\r\n## Installation\r\n\r\n```bash\r\n$ npm install @prftesp/cle-logger\r\n```\r\n\r\n## Features\r\n\r\n- Runs in Node and the browser\r\n- Remote logging\r\n- Multiple custom log sinks\r\n- Remote minimum log level overrides\r\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\r\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\r\n- Support for tagging logs with custom component names or correlation tokens\r\n\r\n## Configuration\r\n\r\nThe logger can be used without configuration, and will by default log to console with a minimum logging level set to ```VERBOSE```.\r\n\r\nTo configure the logger call the ```init``` function and pass in a configuration object\r\n\r\n**Available options**\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// Twilio account sid, required for use with CLE\r\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\r\n\r\n\t// Will be associated with all logs,\r\n\t// if not provided, a unique uuid will be generated\r\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\r\n\t// An array of custom log sinks that will receive a log object,\r\n\t// and can optionally return a promise\r\n\tcustomLogSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: logData => console.log(logData.message),\r\n\t}],\r\n\r\n\t// A way to suppress error messages if set to false, defaults to true\r\n\tlogErrorsToConsole: true,\r\n\r\n\t// To support remote logging, add a url to receive logs\r\n\tlogUrl: \"https://example.com/api/logs\",\r\n\r\n\t// Will not send logs to the default remote log sink configured with 'logUrl'\r\n\tdisableRemoteLogging: false,\r\n\r\n\t// If logging Twilio Flex Actions, provide a list of actions \r\n\t// to be excluded from logs\r\n\texcludedFlexActionNames: [\"SendTyping\"],\r\n\r\n\t// Add console as a log sink, useful for debugging, defaults to true\r\n\tlogToConsole: true,\r\n\r\n\t// Provide a min log level, defaults to INFO\r\n\t// Use cleLogger.logLevels for predefined constants\r\n\tminLogLevel: cleLogger.logLevels.INFO,\r\n\r\n\t// A custom endpoint to fetch log level overrides\r\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level\",\r\n\r\n\t// An instance of ('@twilio/flex-ui').Manager\r\n\ttwilioFlexManager: manager,\r\n});\r\n```\r\n\r\n## API\r\n\r\nThe logger can be imported as an object in CommonJs or ES6 syntax:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\n// or \r\n\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n```\r\n\r\nAlternatively, all top level members of the ```cleLogger``` are available as named exports and can be imported separately:\r\n\r\n```js\r\nimport { info, componentNames } from \"@prftesp/cle-logger\";\r\n\r\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\r\n\r\n// vs\r\n\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\r\n```\r\n\r\n### Logging levels\r\n\r\nLogging level constants can be found in ```cleLogger.logLevels```.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.logLevels.VERBOSE;\r\ncleLogger.logLevels.INFO;\r\ncleLogger.logLevels.WARN;\r\ncleLogger.logLevels.ERROR;\r\ncleLogger.logLevels.CRITICAL;\r\n```\r\n\r\nThere are functions available for each supported logging level:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(...);\r\ncleLogger.info(...);\r\ncleLogger.warn(...);\r\ncleLogger.error(...);\r\ncleLogger.critical(...);\r\n```\r\n\r\nThe functions are all async, return ```Promise<void>```, and can be optionally awaited. The suggested approach is to not await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node backends). It is to suggested to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\r\n\r\n### Logging parameters\r\n\r\nAll logging functions take the same parameters:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(\r\n\t// The only required property, no logs will be generated without it\r\n\t\"Log message\",\r\n\r\n\t// Any additional log data you would like to include with the log,\r\n\t// can be any data type\r\n\t{ data: \"test\" },\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\t// CLE uses it to index these properties in Application Insights\r\n\t// For CLE, try to use keys found in 'cleLogger.customPropertyKeys'\r\n\t{ userId: 123 },\r\n\r\n\t// Another useful way to 'tag' your logs for easier searching\r\n\t// Can be any string, but for CLE, try to use available options in 'cleLogger.componentNames'\r\n\t// If you would like more component names added, please let us know \r\n\t\"ProfileComponent\",\r\n\r\n\t// Log code, can be any number used to categorize logs,\r\n\t// there will be a separate document listing CLE codes\r\n\t70123,\r\n\r\n\t// Correlation token to be associated with this log only,\r\n\t// if one is not provided, the token supplied/generated during 'init' will be used\r\n\t\"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\"\r\n);\r\n\r\n```\r\n\r\n### Available component names\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.componentNames.SESSION;\r\ncleLogger.componentNames.DIALER;\r\ncleLogger.componentNames.DASHBOARD;\r\ncleLogger.componentNames.AFTERCALL;\r\ncleLogger.componentNames.SUPERVISOR;\r\n```\r\n\r\n### Available custom property keys\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.customPropertyKeys.CHANNEL_SID;\r\ncleLogger.customPropertyKeys.TASK_SID;\r\ncleLogger.customPropertyKeys.CALL_SID;\r\ncleLogger.customPropertyKeys.WORKFLOW_SID;\r\ncleLogger.customPropertyKeys.WORKER_SID;\r\n```\r\n\r\n### Registering Twilio Actions\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\n// @param flex { typeof import('@twilio/flex-ui') }\r\ncleLogger.registerTwilioActions(flex);\r\n```\r\n\r\n**Note**: A Twilio account sid must be provided during ```init``` with the ```accountSid``` parameter, otherwise the actions won't be logged.\r\n\r\nThe logger will loop through all actions found under ```flex.Actions.actions```, and attach ```after``` event handlers to log the action name and payload. Any private properties found in the payload (starting with '_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Any actions names configured in ```excludedFlexActionNames``` will not be included in the logs.\r\n\r\n\r\n**Twilio Actions log levels**\r\n\r\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of ```INFO```.\r\n\r\n``` js\r\nAcceptTask: INFO\r\nCancelTransfer: INFO\r\nCompleteTask: INFO\r\nHangupCall: INFO\r\nHideDirectory: VERBOSE\r\nHistoryGo: VERBOSE\r\nHistoryGoBack: VERBOSE\r\nHistoryGoForward: VERBOSE\r\nHistoryPush: VERBOSE\r\nHistoryReplace: VERBOSE\r\nHoldCall: INFO\r\nHoldParticipant: INFO\r\nKickParticipant: INFO\r\nLogout: INFO\r\nMonitorCall: INFO\r\nNavigateToView: INFO\r\nRejectTask: INFO\r\nSelectTask: INFO\r\nSelectTaskInSupervisor: INFO\r\nSelectWorkerInSupervisor: INFO\r\nSendMessage: INFO\r\nSendTyping: VERBOSE\r\nSetActivity: INFO\r\nSetInputText: VERBOSE\r\nShowDirectory: VERBOSE\r\nStopMonitoringCall: INFO\r\nToggleMute: INFO\r\nToggleSidebar: VERBOSE\r\nTransferTask: INFO\r\nUnholdCall: INFO\r\nUnholdParticipant: INFO\r\nWrapupTask: INFO\r\n```\r\n\r\n## Changelog\r\n\r\n### v1.0.0 [2019-11-07]\r\n\r\n- Initial release","readmeFilename":"README.md","_id":"@prftesp/cle-logger@0.0.22-beta.1","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-Br4GxyaJgGzc52FaqD45mGIGRLBIRHwNiRu1KImJv8bWDPkpeB9iHltplUA8N0bl7kh2+c8Nd7ldwblBBgFX9Q==","shasum":"89b2e875b1185dc2d9f7e1c8ab5a8f14f8089299","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.22-beta.1.tgz","fileCount":17,"unpackedSize":42413,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJ11tCRA9TVsSAnZWagAAYMMP/iPhV7PtrVFRTpL27lTy\nat7WnE2H+W0JFODIzviTMswr+pRstpk82UVQiT+gsTJ7H6sPcftR9+8PGVnp\n+KRdojpXRWCPUDUXmMppy3t8P2n3wRWkhgr8M2YfFLB4LOxjp3nftPZf9Lhz\nmpO5bj1GTbcD3ALJ8Y0luVXevGuTrDPWyPoBJUn5Rv5aMBCez2L++9saZ5S+\nmoh1fK/V1HFy3WxwRj40MLrG2yvTnFWyED4Yoy+H7tIR6UlZAjkvArGRhN+x\nf3qQPTqTJ0XA4W+4YKA2SzSCHeTeVAyfKkrSUxUhtjpcRMCLcLY7E3DpIcf5\nzxxwT/lVc3S91wSYBtC0m1aiVZ2iVrW+/1tI7ZPMaCNapb9meyBkX9FUkbgD\n8NlsjH58Ivapt+xP2t2N2khQW+8A8qWRClZ1XfaTpVCzjTHH4bAKSLLyASr1\nnXgsCs4JUe40wj9fCyI2NEuxwVMSzbDpJEuRteeBKnOZANKyKyYKMs63a3pz\njgd1S4gkM+TZVsw8qLhrIh0F9r8xy21iaTs+qeDxvPkPYmEGRVoQPqyHQxcU\nbEA7Yrip0tBChG0cDAccOhPQalbxSRXhhDai55ltaLM3n6tKN+clvbybglbV\nMZr1oY51WiQpg9m6Lc6FUeqUaHrZMwS5MSxbdNOLWZ34quYqf2MkZR8Sn46c\nyoDA\r\n=muOx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIG74GLCG3YBZe5ihGNaPlTMmS8WkJZMJGhLrjwhwQZ1FAiArFGvLZcFU3agYojt9OZzPJVsA6L0XRgotePLZNqSKMQ=="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.22-beta.1_1562860908730_0.7364395219097342"},"_hasShrinkwrap":false},"0.0.22-beta.2":{"name":"@prftesp/cle-logger","version":"0.0.22-beta.2","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿CLE Logger\r\n===\r\n\r\nA configurable, isomorphic javascript remote logging library, with built-in support for Twilio Flex UI.\r\n\r\n## Documentation\r\n\r\n- [Simple Example](#simple-example)\r\n- [Installation](#installation)\r\n- [Features](#features)\r\n- [Configuration](#configuration)\r\n- [API](#api)\r\n\t- [Logging levels](#logging-levels)\r\n\t- [Logging parameters](#logging-parameters)\r\n\t- [Available component names](#available-component-names)\r\n\t- [Available custom property keys](#available-custom-property-keys)\r\n\t- [Registering Twilio Actions](#registering-twilio-actions)\r\n\t- [Version](#version)\r\n- [Log Object](#log-object)\r\n- [Advanced Configuration](#advanced-configuration)\r\n\t- [Log sinks](#log-sinks)\r\n\t- [Remote logging](#remote-logging)\r\n- [Examples](#examples)\r\n- [FAQ](#faq)\r\n- [Changelog](#changelog)\r\n\r\n## Simple Example\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";;\r\n\r\n// will log message to console\r\ncleLogger.info(\"log message\");\r\n```\r\n\r\n## Installation\r\n\r\n```bash\r\n$ npm install @prftesp/cle-logger\r\n```\r\n\r\n## Features\r\n\r\n- Runs in Node and the browser\r\n- Remote logging\r\n- Multiple custom log sinks\r\n- Remote minimum log level overrides\r\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\r\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\r\n- Support for tagging logs with custom component names or correlation tokens\r\n\r\n## Configuration\r\n\r\nThe logger can be used without configuration, and will by default log to console with a minimum logging level set to ```VERBOSE```.\r\n\r\nTo configure the logger call the ```init``` function and pass in a configuration object\r\n\r\n**Available options**\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// Twilio account sid, required for use with CLE\r\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\r\n\r\n\t// Will be associated with all logs,\r\n\t// if not provided, a unique uuid will be generated\r\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\r\n\t// An array of custom log sinks that will receive an array of log objects,\r\n\t// and can optionally return a promise\r\n\tcustomLogSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: logs => console.log(logs),\r\n\t}],\r\n\r\n\t// A way to suppress error messages if set to false, defaults to true\r\n\tlogErrorsToConsole: true,\r\n\r\n\t// To support remote logging, add a url to receive logs\r\n\tlogUrl: \"https://example.com/api/logs\",\r\n\r\n\t// Will not send logs to the default remote log sink configured with 'logUrl'\r\n\tdisableRemoteLogging: false,\r\n\r\n\t// If logging Twilio Flex Actions, provide a list of actions \r\n\t// to be excluded from logs\r\n\texcludedFlexActionNames: [\"SendTyping\"],\r\n\r\n\t// Add console as a log sink, useful for debugging, defaults to true\r\n\tlogToConsole: true,\r\n\r\n\t// Provide a min log level, defaults to INFO\r\n\t// Use cleLogger.logLevels for predefined constants\r\n\tminLogLevel: cleLogger.logLevels.INFO,\r\n\r\n\t// A custom endpoint to fetch log level overrides\r\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level\",\r\n\r\n\t// An instance of ('@twilio/flex-ui').Manager\r\n\ttwilioFlexManager: manager,\r\n});\r\n```\r\n\r\n## API\r\n\r\nThe logger can be imported as an object in CommonJs or ES6 syntax:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n// or\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n```\r\n\r\nAlternatively, all top level members of the ```cleLogger``` are available as named exports and can be imported separately:\r\n\r\n```js\r\nimport { info, componentNames } from \"@prftesp/cle-logger\";\r\n\r\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\r\n// vs\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\r\n```\r\n\r\n### Logging levels\r\n\r\nLogging level constants can be found in ```cleLogger.logLevels```.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.logLevels.VERBOSE;\r\ncleLogger.logLevels.INFO;\r\ncleLogger.logLevels.WARN;\r\ncleLogger.logLevels.ERROR;\r\ncleLogger.logLevels.CRITICAL;\r\n```\r\n\r\nThere are functions available for each supported logging level:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(...);\r\ncleLogger.info(...);\r\ncleLogger.warn(...);\r\ncleLogger.error(...);\r\ncleLogger.critical(...);\r\n```\r\n\r\nThe functions are all async, return ```Promise<void>```, and can be optionally awaited. The suggested approach is to not await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node backends). It is to suggested to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\r\n\r\n### Logging parameters\r\n\r\nAll logging functions take the same parameters:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(\r\n\t// The only required property, no logs will be generated without it\r\n\t\"Log message\",\r\n\r\n\t// Any additional log data you would like to include with the log,\r\n\t// can be any data type\r\n\t{ data: \"test\" },\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\t// CLE uses it to index these properties in Application Insights\r\n\t// For CLE, try to use keys found in 'cleLogger.customPropertyKeys'\r\n\t{ userId: 123 },\r\n\r\n\t// Another useful way to 'tag' your logs for easier searching\r\n\t// Can be any string, but for CLE, try to use available options in 'cleLogger.componentNames'\r\n\t// If you would like more component names added, please let us know \r\n\t\"ProfileComponent\",\r\n\r\n\t// Log code, can be any number used to categorize logs,\r\n\t// there will be a separate document listing CLE codes\r\n\t70123,\r\n\r\n\t// Correlation token to be associated with this log only,\r\n\t// if one is not provided, the token supplied/generated during 'init' will be used\r\n\t\"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\"\r\n);\r\n```\r\n\r\n### Available component names\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.componentNames.SESSION;\r\ncleLogger.componentNames.DIALER;\r\ncleLogger.componentNames.DASHBOARD;\r\ncleLogger.componentNames.AFTERCALL;\r\ncleLogger.componentNames.SUPERVISOR;\r\n```\r\n\r\n### Available custom property keys\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.customPropertyKeys.CHANNEL_SID;\r\ncleLogger.customPropertyKeys.TASK_SID;\r\ncleLogger.customPropertyKeys.CALL_SID;\r\ncleLogger.customPropertyKeys.WORKFLOW_SID;\r\ncleLogger.customPropertyKeys.WORKER_SID;\r\n```\r\n\r\n### Registering Twilio Actions\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\n// @param flex { typeof import('@twilio/flex-ui') }\r\ncleLogger.registerTwilioActions(flex);\r\n```\r\n\r\n**Note**: A Twilio account sid must be provided during ```init``` with the ```accountSid``` parameter, otherwise the actions won't be logged.\r\n\r\nThe logger will loop through all actions found under ```flex.Actions.actions```, and attach ```after``` event handlers to log the action name and payload. Any private properties found in the payload (starting with '_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Any actions names configured in ```excludedFlexActionNames``` will not be included in the logs.\r\n\r\n**Twilio Actions log levels**\r\n\r\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of ```INFO```.\r\n\r\n``` js\r\nAcceptTask: INFO\r\nCancelTransfer: INFO\r\nCompleteTask: INFO\r\nHangupCall: INFO\r\nHideDirectory: VERBOSE\r\nHistoryGo: VERBOSE\r\nHistoryGoBack: VERBOSE\r\nHistoryGoForward: VERBOSE\r\nHistoryPush: VERBOSE\r\nHistoryReplace: VERBOSE\r\nHoldCall: INFO\r\nHoldParticipant: INFO\r\nKickParticipant: INFO\r\nLogout: INFO\r\nMonitorCall: INFO\r\nNavigateToView: INFO\r\nRejectTask: INFO\r\nSelectTask: INFO\r\nSelectTaskInSupervisor: INFO\r\nSelectWorkerInSupervisor: INFO\r\nSendMessage: INFO\r\nSendTyping: VERBOSE\r\nSetActivity: INFO\r\nSetInputText: VERBOSE\r\nShowDirectory: VERBOSE\r\nStopMonitoringCall: INFO\r\nToggleMute: INFO\r\nToggleSidebar: VERBOSE\r\nTransferTask: INFO\r\nUnholdCall: INFO\r\nUnholdParticipant: INFO\r\nWrapupTask: INFO\r\n```\r\n\r\n### Version\r\n\r\nVersion will be automatically included in all logs.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.version; // 1.0.0\r\n```\r\n\r\n## Log Object\r\n\r\n```ts\r\ninterface LogData {\r\n\t// Account sid if provided during init\r\n\taccountSid: string;\r\n\r\n\t// Any additional log data to be included with the log\r\n\tadditionalData: unknown;\r\n\t\r\n\t// Component name, if provided\r\n\tcomponentName: string;\r\n\r\n\t// Generated or configured token\r\n\tcorrelationToken: string;\r\n\r\n\t// Current UTC datetime in ISO 8601 format\r\n\teventDate: string;\r\n\r\n\t// Log level\r\n\tlevel: string;\r\n\r\n\t// Log code, if provided\r\n\tlogCode: number;\r\n\r\n\t// Log message\r\n\tmessage: string;\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\tproperties: object;\r\n\r\n\t// BrowserFunction if the environment is the browser\r\n\t// CloudFunction if the environment is Node\r\n\tsource: string;\r\n\r\n\t// Logging library version \r\n\tloggerVersion: string;\r\n}\r\n```\r\n\r\n## Advanced Configuration\r\n\r\n### Log sinks\r\n\r\nCustom log sinks can be provided via the ```customLogSinks``` config. It's an array of objects containing a ```name``` and a ```log``` method. All provided log sinks, along with the built-in remote and console log sinks will be executed in parallel. You can choose to await the logs to make sure all logging operations are completed.\r\nThe ```log``` method will receive an array of ```LogData``` objects for you to handle. If you're doing async requests in your custom log sinks, it is recommended to return the Promise.\r\n\r\nExample for logging to file in Node:\r\n\r\n``` js\r\nconst fs = require(\"fs\");\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\ncleLogger.init({\r\n\tcustomLogSinks: [{\r\n      name: \"testFileSink\",\r\n      log: logs => { \r\n\t\t\t\tfs.appendFile(\r\n\t\t\t\t\t\"./log.txt\", \r\n\t\t\t\t\t`${JSON.stringify(logs)}`, \r\n\t\t\t\t\tconsole.log\r\n\t\t\t\t); \r\n\t\t\t}    \r\n  }]\r\n});\r\n```\r\n\r\n### Remote logging\r\n\r\n**Remote log endpoint**\r\n\r\n\r\n**Remote log level override**\r\n\r\n## Examples\r\n\r\n\r\n## FAQ\r\n\r\n\r\n## Changelog\r\n\r\n### v1.0.0 [2019-11-07]\r\n\r\n- Initial release","readmeFilename":"README.md","_id":"@prftesp/cle-logger@0.0.22-beta.2","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-co6iPZFl6bVNj2rsgHYw3QKGNhFWhiNCHFpBB1sFk0kqM1l807Z2BuE8mryyAbta0yAJXfcvrjWf1sHxAsk60A==","shasum":"675cd301f5eac643bd03893d9a0c70e676c24b4a","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.22-beta.2.tgz","fileCount":17,"unpackedSize":44656,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJ3M/CRA9TVsSAnZWagAAaW4P/jZwXABxkLIrT4AoDV+X\nwJ8q3/9iu9Oq6OYVAUL4qmPe0QVRYhCHt45NJ8SEimkCRJmgNVugz0UMYn7X\n7/1pjBJINHofJYiXKW/4A91s8fhoXBf1oi4XVnHzunAUIBbycfkLlymMIJOh\nq19yYK0giMwPVC9Yk2ZWg9ctkS+1GdxQe0z5XmKHNdlw850i5CZnEnKDPHe4\nWY1xxW2eXsMfuuQZMg/P8s3ZvBw9T+eSAe8e3nLXKC5dDV/I89dnx8TOxFzW\nEt5RKYhEF2UDfzLnvidqlSYjqenI3YI0GR6f5pKm1J4rlfnsxnjKsFC8zzQP\nchtsaiYCtW9HLlUWJgw9984FmTbtHuSJuAKR3xY9giKTcS2yizSGr/w9kIto\nBhCts1ikBYzlQfLSdpUCodq0xYSXojCoMkVUluH5ova3hK/w0F3pYXK0Kysd\nfJ9kUXEkdADMFCkQ/ep0r75IOBuCCImepXMSJXDEa6CtjK0Zi3SEOul6Bmfi\nvDcmgl/DD52tk1c+9kJgjL+2IP22ZmEMh+EpqaLHEKLIbuFoRMU1QW+Wze31\nNIZlv67DZw4Ih1ZUNn8WDTFJkpKlNjMu7PkPg3x98xRg6ky/xVTVik2P+Elg\nkXQ5DkZM3Kwh3+ackGw3e0+WAigQSEAsJlb6XlwN961+YSh8gyrmsbCV8j6F\n7O+q\r\n=5LaE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBOzbZmYlyjvhgTFWRsCZ1ImdjFXTfckMHrE6Vb/L7AkAiEA4YqO6nQ1rndaslh6Jockk6tK9DIrLPnJYJo/h1ghmB0="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.22-beta.2_1562866494930_0.9115710172570761"},"_hasShrinkwrap":false},"0.0.22-beta.3":{"name":"@prftesp/cle-logger","version":"0.0.22-beta.3","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿CLE Logger\r\n===\r\n\r\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\r\n\r\n## Documentation\r\n\r\n- [Target Environment](#target-environment)\r\n- [Simple Example](#simple-example)\r\n- [Installation](#installation)\r\n- [Features](#features)\r\n- [Configuration](#configuration)\r\n- [API](#api)\r\n\t- [Logging levels](#logging-levels)\r\n\t- [Logging parameters](#logging-parameters)\r\n\t- [Available component names](#available-component-names)\r\n\t- [Available custom property keys](#available-custom-property-keys)\r\n\t- [Registering Twilio Actions](#registering-twilio-actions)\r\n\t- [Version](#version)\r\n- [Log Object](#log-object)\r\n- [Advanced Configuration](#advanced-configuration)\r\n\t- [Log sinks](#log-sinks)\r\n\t- [Remote logging](#remote-logging)\r\n- [Examples](#examples)\r\n- [FAQ](#faq)\r\n- [Changelog](#changelog)\r\n\r\n## Target Environment\r\n\r\nAny Node environment capable of running and/or transpiling code compatible with Node v8.10 or above.\r\n\r\n## Simple Example\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";;\r\n\r\n// will log message to console\r\ncleLogger.info(\"log message\");\r\n```\r\n\r\n## Installation\r\n\r\n```bash\r\n$ npm install @prftesp/cle-logger\r\n```\r\n\r\n## Features\r\n\r\n- Runs in Node and the browser\r\n- Remote logging\r\n- Multiple custom log sinks\r\n- Remote minimum log level overrides\r\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\r\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\r\n- Support for tagging logs with custom component names or correlation tokens\r\n\r\n## Configuration\r\n\r\nThe logger can be used without configuration, and will by default log to console with a minimum logging level set to ```VERBOSE```.\r\n\r\nTo configure the logger call the ```init``` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\r\n\r\n**Available options**\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// Twilio account sid, required for use with CLE\r\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\r\n\r\n\t// Will be associated with all logs,\r\n\t// if not provided, a unique uuid will be generated\r\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\r\n\t// An array of custom log sinks that will receive an array of log objects,\r\n\t// and can optionally return a promise\r\n\tcustomLogSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: logs => console.log(logs),\r\n\t}],\r\n\r\n\t// A way to suppress error messages if set to false, defaults to true\r\n\tlogErrorsToConsole: true,\r\n\r\n\t// To support remote logging, add a url to receive logs\r\n\tlogUrl: \"https://example.com/api/logs\",\r\n\r\n\t// Will not send logs to the default remote log sink configured with 'logUrl'\r\n\tdisableRemoteLogging: false,\r\n\r\n\t// If logging Twilio Flex Actions, provide a list of actions \r\n\t// to be excluded from logs\r\n\texcludedFlexActionNames: [\"SendTyping\"],\r\n\r\n\t// Add console as a log sink, useful for debugging, defaults to true\r\n\tlogToConsole: true,\r\n\r\n\t// Provide a min log level, defaults to INFO\r\n\t// Use cleLogger.logLevels for predefined constants\r\n\tminLogLevel: cleLogger.logLevels.INFO,\r\n\r\n\t// A custom endpoint to fetch log level overrides\r\n\t// NOTE: the provided url should always end with 'accountSid=',\r\n\t// the library will append the accountSid\r\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\r\n\r\n\t// An instance of ('@twilio/flex-ui').Manager\r\n\ttwilioFlexManager: manager,\r\n});\r\n```\r\n\r\n## API\r\n\r\nThe logger can be imported as an object in CommonJs or ES6 syntax:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n// or\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n```\r\n\r\nAlternatively, all top level members of the ```cleLogger``` are available as named exports and can be imported separately:\r\n\r\n```js\r\nimport { info, componentNames } from \"@prftesp/cle-logger\";\r\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\r\n\r\n// vs\r\n\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\r\n```\r\n\r\n### Logging levels\r\n\r\nLogging level constants can be found in ```cleLogger.logLevels```.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.logLevels.VERBOSE; // verbose\r\ncleLogger.logLevels.INFO; // info\r\ncleLogger.logLevels.WARN; // warn\r\ncleLogger.logLevels.ERROR; // error\r\ncleLogger.logLevels.CRITICAL; // critical\r\n```\r\n\r\nThere are functions available for each supported logging level:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(...);\r\ncleLogger.info(...);\r\ncleLogger.warn(...);\r\ncleLogger.error(...);\r\ncleLogger.critical(...);\r\n```\r\n\r\nThe functions are all async, return ```Promise<void>```, and can be optionally awaited. The suggested approach is to not await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node backends). It is to suggested to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\r\n\r\n### Logging parameters\r\n\r\nAll logging functions take the same parameters:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(\r\n\t// The only required property, no logs will be generated without it\r\n\t\"Log message\",\r\n\r\n\t// Any additional log data you would like to include with the log,\r\n\t// can be any data type\r\n\t{ data: \"test\" },\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\t// CLE uses it to index these properties in Application Insights\r\n\t// For CLE, try to use keys found in 'cleLogger.customPropertyKeys'\r\n\t{ userId: 123 },\r\n\r\n\t// Another useful way to 'tag' your logs for easier searching\r\n\t// Can be any string, but for CLE, try to use available options in 'cleLogger.componentNames'\r\n\t// If you would like more component names added, please let us know \r\n\t\"ProfileComponent\",\r\n\r\n\t// Log code, can be any number used to categorize logs,\r\n\t// there will be a separate document listing CLE codes\r\n\t70123,\r\n\r\n\t// Correlation token to be associated with this log only,\r\n\t// if one is not provided, the token supplied/generated during 'init' will be used\r\n\t\"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\"\r\n);\r\n```\r\n\r\n### Available component names\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.componentNames.SESSION;\r\ncleLogger.componentNames.DIALER;\r\ncleLogger.componentNames.DASHBOARD;\r\ncleLogger.componentNames.AFTERCALL;\r\ncleLogger.componentNames.SUPERVISOR;\r\n```\r\n\r\n### Available custom property keys\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.customPropertyKeys.CHANNEL_SID;\r\ncleLogger.customPropertyKeys.TASK_SID;\r\ncleLogger.customPropertyKeys.CALL_SID;\r\ncleLogger.customPropertyKeys.WORKFLOW_SID;\r\ncleLogger.customPropertyKeys.WORKER_SID;\r\n```\r\n\r\n### Registering Twilio Actions\r\n\r\nExample in a Twilio Flex UI plugin:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\n/**\r\n * @param flex { typeof import('@twilio/flex-ui') }\r\n * @param manager { import('@twilio/flex-ui').Manager }\r\n */\r\ninit(flex, manager) {\r\n\tcleLogger.init({\r\n\t\taccountSid: \"your-account-sid\"\r\n\t});\r\n\t\r\n\tcleLogger.registerTwilioActions(flex);\r\n}\r\n```\r\n\r\n**Note**: A Twilio account sid must be provided during ```init``` with the ```accountSid``` parameter, otherwise the actions won't be logged.\r\n\r\nThe logger will loop through all actions found under ```flex.Actions.actions```, and attach ```after``` event handlers to log the action name and payload. Any private properties found in the payload (starting with '_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Any actions names configured in ```excludedFlexActionNames``` will not be included in the logs.\r\n\r\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated for this purpose.\r\n\r\n**Twilio Actions log levels**\r\n\r\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of ```INFO```.\r\n\r\n``` js\r\nAcceptTask: INFO\r\nCancelTransfer: INFO\r\nCompleteTask: INFO\r\nHangupCall: INFO\r\nHideDirectory: VERBOSE\r\nHistoryGo: VERBOSE\r\nHistoryGoBack: VERBOSE\r\nHistoryGoForward: VERBOSE\r\nHistoryPush: VERBOSE\r\nHistoryReplace: VERBOSE\r\nHoldCall: INFO\r\nHoldParticipant: INFO\r\nKickParticipant: INFO\r\nLogout: INFO\r\nMonitorCall: INFO\r\nNavigateToView: INFO\r\nRejectTask: INFO\r\nSelectTask: INFO\r\nSelectTaskInSupervisor: INFO\r\nSelectWorkerInSupervisor: INFO\r\nSendMessage: INFO\r\nSendTyping: VERBOSE\r\nSetActivity: INFO\r\nSetInputText: VERBOSE\r\nShowDirectory: VERBOSE\r\nStopMonitoringCall: INFO\r\nToggleMute: INFO\r\nToggleSidebar: VERBOSE\r\nTransferTask: INFO\r\nUnholdCall: INFO\r\nUnholdParticipant: INFO\r\nWrapupTask: INFO\r\n```\r\n\r\n### Version\r\n\r\nVersion will be automatically included in all logs.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.version; // 1.0.0\r\n```\r\n\r\n## Log Object\r\n\r\n```js\r\n{\r\n\t// Account sid if provided during init\r\n\taccountSid: string;\r\n\r\n\t// Any additional log data to be included with the log\r\n\tadditionalData: unknown;\r\n\t\r\n\t// Component name, if provided\r\n\tcomponentName: string;\r\n\r\n\t// Generated or configured token\r\n\tcorrelationToken: string;\r\n\r\n\t// Current UTC datetime in ISO 8601 format\r\n\teventDate: string;\r\n\r\n\t// Log level\r\n\tlevel: string;\r\n\r\n\t// Log code, if provided\r\n\tlogCode: number;\r\n\r\n\t// Log message\r\n\tmessage: string;\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\tproperties: object;\r\n\r\n\t// BrowserFunction if the environment is the browser\r\n\t// CloudFunction if the environment is Node\r\n\tsource: string;\r\n\r\n\t// Logging library version \r\n\tloggerVersion: string;\r\n}\r\n```\r\n\r\n## Advanced Configuration\r\n\r\n### Log sinks\r\n\r\nCustom log sinks can be provided via the ```customLogSinks``` config. It's an array of objects containing a ```name``` and a ```log``` method. All provided log sinks, along with the built-in remote and console log sinks will be executed in parallel. You can choose to await the logs to make sure all logging operations are completed.\r\nThe ```log``` method will receive an array of log objects for you to handle. If you're doing async requests in your custom log sinks, it is recommended to return the Promise.\r\n\r\nExample logging to file in Node:\r\n\r\n``` js\r\nconst fs = require(\"fs\");\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\ncleLogger.init({\r\n\tcustomLogSinks: [{\r\n\t\tname: \"testFileSink\",\r\n\t\tlog: logs => {\r\n\t\t\tfs.appendFile(\r\n\t\t\t\t\"./log.txt\",\r\n\t\t\t\t`${JSON.stringify(logs)}`,\r\n\t\t\t\tconsole.log\r\n\t\t\t);\r\n\t\t}\r\n\t}]\r\n});\r\n```\r\n\r\n### Remote logging\r\n\r\n**Remote log endpoint**\r\n\r\nTo implement your own remote logging endpoint, all you have to do is accept ```POST``` requests, with a JSON payload of an array of [log objects](#log-object), and return either ```{ success: true }``` or ```{ success: false }```. The endpoint should always return a ```200 OK``` status code. A good use-case would be leveraging a serverless api platform to output logs to a queue.\r\n\r\nExample Azure Function:\r\n\r\n```js\r\nmodule.exports = async function (context, req) {\r\n    context.log(req.body); // body will have the log array\r\n\r\n    context.res = {\r\n        status: 200,\r\n        body: { success: true }\r\n    };\r\n};\r\n```\r\n\r\n**Remote log level override**\r\n\r\nThe ```minLogLevelUrl``` configuration property is used to provide an endpoint that accepts ```GET``` requests and a ```accountSid``` query string parameter. ```accountSid``` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the ```minLogLevel``` configuration property. If null is returned, the ```minLogLevel``` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a ```200 OK``` status code and use the ```success``` property to indicate failure.\r\n\r\nExample success response:\r\n\r\n```json\r\n{ \r\n\t\"success\": true,\r\n\t\"minLogLevel\": \"warn\"\r\n}\r\n```\r\n\r\nExample error response:\r\n\r\n```json\r\n{ \r\n\t\"success\": false,\r\n\t\"minLogLevel\": null\r\n}\r\n```\r\n\r\n## Changelog\r\n\r\n### v1.0.0 [2019-11-07]\r\n\r\n- Initial release","readmeFilename":"README.md","_id":"@prftesp/cle-logger@0.0.22-beta.3","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-EUreoKdX/3iGYI13pI0M6N5G/tS83WT4YPfjlH15oovGUmYwYi0EBwfF5oLQioyR2lJJ7yZH+uQxQctTt37x1A==","shasum":"d4ac3fe75cdbcd41ad1252ec04a55f4f9a334abe","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.22-beta.3.tgz","fileCount":17,"unpackedSize":47121,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJ3rKCRA9TVsSAnZWagAAY4EQAJlY+Dt/iM8RarMoh6fS\n8Y+9zSU2omkCnbzxOIRzIKPzHrydJYv7lRvJa6uNFGO5XMsNiVp25r+ggorm\nxBsTSP7LO3N3QxUkSJoF4+KtXaxVX5dHgWLqrmf2MD14exRGIYobuu6mqiu2\nzbo9sJyfkLj9vjQkU/TC54m/Npgh87uHgz+lIUec8XU4UsfymePOVlDuct+Q\n8BCxBgMopO+QYzcO3Ud4+WkQL2tRUPpI4kCkjIq0zm450TU9lqbu9G78oyRb\nIIuqreCBcU6Ts8qeSR60+5d/ZqBLM0dfYAWd97gtQs1Gd1x6up/QHIde1t3i\np7JoGPdmO+e3wn9QPMaXBy9Mhdz5cFRKl9Q2+HH5mEWAKVsni+Q4bwew8DTV\nw+m3PH/o30xQNdJM+r3OfRL7tdSx61QeivL3LjCEaOcdsEHqhf6Tznc+cdrv\nSdIOETcs80ZAm2fLr5nrVj1Wey9QoSYMzjVj6r4z+uRZzeDGVYND3RjYMqPj\nzAlHM3iDRQWTAsJeXz3PKpWn84DlZTqhG5mwI8OXMEHgEHifb90PvurhTtsw\nfzdiWw9UCjokfVR87gK4jo3nXRfq4yGe8t0mPisOq9zF05yzEwcaMrjoNhjO\ny7GBs4JIXaTQkIQgEEb14BhwnPHxb1CmM2KR0znI/fGL3snEt4aUXF2vT1jT\ncOcz\r\n=Fngx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDHW8IOHVoyYHjVDaBywBGJzQIIiQHYxEHnwx/bFiwjswIgCSOapS9lyTYmz6UePTA8Pc/tzCYNEuCL5bYJsqkevHk="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.22-beta.3_1562868426140_0.2470449131521566"},"_hasShrinkwrap":false},"0.0.22-beta.4":{"name":"@prftesp/cle-logger","version":"0.0.22-beta.4","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿CLE Logger\r\n===\r\n\r\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\r\n\r\n## Documentation\r\n\r\n- [Target Environment](#target-environment)\r\n- [Simple Example](#simple-example)\r\n- [Installation](#installation)\r\n- [Features](#features)\r\n- [Configuration](#configuration)\r\n- [API](#api)\r\n\t- [Logging levels](#logging-levels)\r\n\t- [Logging parameters](#logging-parameters)\r\n\t- [Available component names](#available-component-names)\r\n\t- [Available custom property keys](#available-custom-property-keys)\r\n\t- [Registering Twilio Actions](#registering-twilio-actions)\r\n\t- [Version](#version)\r\n- [Log Object](#log-object)\r\n- [Advanced Configuration](#advanced-configuration)\r\n\t- [Log sinks](#log-sinks)\r\n\t- [Remote logging](#remote-logging)\r\n- [Changelog](#changelog)\r\n\r\n## Target Environment\r\n\r\nAny Node environment capable of running and/or transpiling code compatible with Node v8.10 or above.\r\n\r\n## Simple Example\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";;\r\n\r\n// will log message to console\r\ncleLogger.info(\"log message\");\r\n```\r\n\r\n## Installation\r\n\r\n```bash\r\n$ npm install @prftesp/cle-logger\r\n```\r\n\r\n## Features\r\n\r\n- Runs in Node and the browser\r\n- Remote logging\r\n- Multiple custom log sinks\r\n- Remote minimum log level overrides\r\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\r\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\r\n- Support for tagging logs with custom component names or correlation tokens\r\n\r\n## Configuration\r\n\r\nThe logger can be used without configuration, and will by default log to console with a minimum logging level set to ```VERBOSE```.\r\n\r\nTo configure the logger call the ```init``` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\r\n\r\n**Available options**\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// Twilio account sid, required for use with CLE\r\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\r\n\r\n\t// Will be associated with all logs,\r\n\t// if not provided, a unique uuid will be generated\r\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\r\n\t// An array of custom log sinks that will receive an array of log objects,\r\n\t// and can optionally return a promise\r\n\tcustomLogSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: logs => console.log(logs),\r\n\t}],\r\n\r\n\t// A way to suppress error messages if set to false, defaults to true\r\n\tlogErrorsToConsole: true,\r\n\r\n\t// To support remote logging, add a url to receive logs\r\n\tlogUrl: \"https://example.com/api/logs\",\r\n\r\n\t// Will not send logs to the default remote log sink configured with 'logUrl'\r\n\tdisableRemoteLogging: false,\r\n\r\n\t// If logging Twilio Flex Actions, provide a list of actions \r\n\t// to be excluded from logs\r\n\texcludedFlexActionNames: [\"SendTyping\"],\r\n\r\n\t// Add console as a log sink, useful for debugging, defaults to true\r\n\tlogToConsole: true,\r\n\r\n\t// Provide a min log level, defaults to INFO\r\n\t// Use cleLogger.logLevels for predefined constants\r\n\tminLogLevel: cleLogger.logLevels.INFO,\r\n\r\n\t// A custom endpoint to fetch log level overrides\r\n\t// NOTE: the provided url should always end with 'accountSid=',\r\n\t// the library will append the accountSid\r\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\r\n\r\n\t// An instance of ('@twilio/flex-ui').Manager\r\n\ttwilioFlexManager: manager,\r\n});\r\n```\r\n\r\n## API\r\n\r\nThe logger can be imported as an object in CommonJs or ES6 syntax:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n// or\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n```\r\n\r\nAlternatively, all top level members of the ```cleLogger``` are available as named exports and can be imported separately:\r\n\r\n```js\r\nimport { info, componentNames } from \"@prftesp/cle-logger\";\r\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\r\n\r\n// vs\r\n\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\r\n```\r\n\r\n### Logging levels\r\n\r\nLogging level constants can be found in ```cleLogger.logLevels```.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.logLevels.VERBOSE; // verbose\r\ncleLogger.logLevels.INFO; // info\r\ncleLogger.logLevels.WARN; // warn\r\ncleLogger.logLevels.ERROR; // error\r\ncleLogger.logLevels.CRITICAL; // critical\r\n```\r\n\r\nThere are functions available for each supported logging level:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(...);\r\ncleLogger.info(...);\r\ncleLogger.warn(...);\r\ncleLogger.error(...);\r\ncleLogger.critical(...);\r\n```\r\n\r\nThe functions are all async, return ```Promise<void>```, and can be optionally awaited. The suggested approach is to not await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node backends). It is to suggested to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\r\n\r\n### Logging parameters\r\n\r\nAll logging functions take the same parameters:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(\r\n\t// The only required property, no logs will be generated without it\r\n\t\"Log message\",\r\n\r\n\t// Any additional log data you would like to include with the log,\r\n\t// can be any data type\r\n\t{ data: \"test\" },\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\t// CLE uses it to index these properties in Application Insights\r\n\t// For CLE, try to use keys found in 'cleLogger.customPropertyKeys'\r\n\t{ userId: 123 },\r\n\r\n\t// Another useful way to 'tag' your logs for easier searching\r\n\t// Can be any string, but for CLE, try to use available options in 'cleLogger.componentNames'\r\n\t// If you would like more component names added, please let us know \r\n\t\"ProfileComponent\",\r\n\r\n\t// Log code, can be any number used to categorize logs,\r\n\t// there will be a separate document listing CLE codes\r\n\t70123,\r\n\r\n\t// Correlation token to be associated with this log only,\r\n\t// if one is not provided, the token supplied/generated during 'init' will be used\r\n\t\"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\"\r\n);\r\n```\r\n\r\n### Available component names\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.componentNames.SESSION;\r\ncleLogger.componentNames.DIALER;\r\ncleLogger.componentNames.DASHBOARD;\r\ncleLogger.componentNames.AFTERCALL;\r\ncleLogger.componentNames.SUPERVISOR;\r\n```\r\n\r\n### Available custom property keys\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.customPropertyKeys.CHANNEL_SID;\r\ncleLogger.customPropertyKeys.TASK_SID;\r\ncleLogger.customPropertyKeys.CALL_SID;\r\ncleLogger.customPropertyKeys.WORKFLOW_SID;\r\ncleLogger.customPropertyKeys.WORKER_SID;\r\n```\r\n\r\n### Registering Twilio Actions\r\n\r\nExample in a Twilio Flex UI plugin:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\n/**\r\n * @param flex { typeof import('@twilio/flex-ui') }\r\n * @param manager { import('@twilio/flex-ui').Manager }\r\n */\r\ninit(flex, manager) {\r\n\tcleLogger.init({\r\n\t\taccountSid: \"your-account-sid\"\r\n\t});\r\n\r\n\tcleLogger.registerTwilioActions(flex);\r\n}\r\n```\r\n\r\n**Note**: A Twilio account sid must be provided during ```init``` with the ```accountSid``` parameter, otherwise the actions won't be logged.\r\n\r\nThe logger will loop through all actions found under ```flex.Actions.actions```, and attach ```after``` event handlers to log the action name and payload. Any private properties found in the payload (starting with '_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Any actions names configured in ```excludedFlexActionNames``` will not be included in the logs.\r\n\r\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated for this purpose.\r\n\r\n**Twilio Actions log levels**\r\n\r\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of ```INFO```.\r\n\r\n``` js\r\nAcceptTask: INFO\r\nCancelTransfer: INFO\r\nCompleteTask: INFO\r\nHangupCall: INFO\r\nHideDirectory: VERBOSE\r\nHistoryGo: VERBOSE\r\nHistoryGoBack: VERBOSE\r\nHistoryGoForward: VERBOSE\r\nHistoryPush: VERBOSE\r\nHistoryReplace: VERBOSE\r\nHoldCall: INFO\r\nHoldParticipant: INFO\r\nKickParticipant: INFO\r\nLogout: INFO\r\nMonitorCall: INFO\r\nNavigateToView: INFO\r\nRejectTask: INFO\r\nSelectTask: INFO\r\nSelectTaskInSupervisor: INFO\r\nSelectWorkerInSupervisor: INFO\r\nSendMessage: INFO\r\nSendTyping: VERBOSE\r\nSetActivity: INFO\r\nSetInputText: VERBOSE\r\nShowDirectory: VERBOSE\r\nStopMonitoringCall: INFO\r\nToggleMute: INFO\r\nToggleSidebar: VERBOSE\r\nTransferTask: INFO\r\nUnholdCall: INFO\r\nUnholdParticipant: INFO\r\nWrapupTask: INFO\r\n```\r\n\r\n### Version\r\n\r\nVersion will be automatically included in all logs.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.version; // 1.0.0\r\n```\r\n\r\n## Log Object\r\n\r\n```js\r\n{\r\n\t// Account sid if provided during init\r\n\taccountSid: string;\r\n\r\n\t// Any additional log data to be included with the log\r\n\tadditionalData: unknown;\r\n\t\r\n\t// Component name, if provided\r\n\tcomponentName: string;\r\n\r\n\t// Generated or configured token\r\n\tcorrelationToken: string;\r\n\r\n\t// Current UTC datetime in ISO 8601 format\r\n\teventDate: string;\r\n\r\n\t// Log level\r\n\tlevel: string;\r\n\r\n\t// Log code, if provided\r\n\tlogCode: number;\r\n\r\n\t// Log message\r\n\tmessage: string;\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\tproperties: object;\r\n\r\n\t// BrowserFunction if the environment is the browser\r\n\t// CloudFunction if the environment is Node\r\n\tsource: string;\r\n\r\n\t// Logging library version \r\n\tloggerVersion: string;\r\n}\r\n```\r\n\r\n## Advanced Configuration\r\n\r\n### Log sinks\r\n\r\nCustom log sinks can be provided via the ```customLogSinks``` config. It's an array of objects containing a ```name``` and a ```log``` method. All provided log sinks, along with the built-in remote and console log sinks will be executed in parallel. You can choose to await the logs to make sure all logging operations are completed.\r\nThe ```log``` method will receive an array of log objects for you to handle. If you're doing async requests in your custom log sinks, it is recommended to return the Promise.\r\n\r\nExample logging to file in Node:\r\n\r\n``` js\r\nconst fs = require(\"fs\");\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\ncleLogger.init({\r\n\tcustomLogSinks: [{\r\n\t\tname: \"testFileSink\",\r\n\t\tlog: logs => {\r\n\t\t\tfs.appendFile(\r\n\t\t\t\t\"./log.txt\",\r\n\t\t\t\t`${JSON.stringify(logs)}`,\r\n\t\t\t\tconsole.log\r\n\t\t\t);\r\n\t\t}\r\n\t}]\r\n});\r\n```\r\n\r\n### Remote logging\r\n\r\nThe library provides a way to provide you own remote log url, or to remotely override the min log level. This can be useful for temporarily setting the log level to verbose in a production environment, for quick troubleshooting.\r\n\r\n**Remote log endpoint**\r\n\r\nTo implement your own remote logging endpoint, all you have to do is accept ```POST``` requests, with a JSON payload of an array of [log objects](#log-object), and return either ```{ success: true }``` or ```{ success: false }```. The endpoint should always return a ```200 OK``` status code. A good use-case would be leveraging a serverless api platform to output logs to a queue.\r\n\r\nExample Azure Function:\r\n\r\n```js\r\nmodule.exports = async function (context, req) {\r\n    context.log(req.body); // body will have the log array\r\n\r\n    context.res = {\r\n        status: 200,\r\n        body: { success: true }\r\n    };\r\n};\r\n```\r\n\r\n**Remote log level override**\r\n\r\nThe ```minLogLevelUrl``` configuration property is used to provide an endpoint that accepts ```GET``` requests and a ```accountSid``` query string parameter. ```accountSid``` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the ```minLogLevel``` configuration property. If null is returned, the ```minLogLevel``` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a ```200 OK``` status code and use the ```success``` property to indicate failure.\r\n\r\nExample success response:\r\n\r\n```json\r\n{ \r\n\t\"success\": true,\r\n\t\"minLogLevel\": \"warn\"\r\n}\r\n```\r\n\r\nExample error response:\r\n\r\n```json\r\n{ \r\n\t\"success\": false,\r\n\t\"minLogLevel\": null\r\n}\r\n```\r\n\r\n## Changelog\r\n\r\n### v1.0.0 [2019-11-07]\r\n\r\n- Initial release","readmeFilename":"README.md","_id":"@prftesp/cle-logger@0.0.22-beta.4","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-GGp7KHoY4ty1vm6pOqmYAdv/g0ZbWWMszBII0AxIUWrU4Qv7erVtCTjEggoBvhvjHaaXo8GiMQOjfJVz4VllwA==","shasum":"1c0c792c5a699a5460572485adf186590e096534","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.22-beta.4.tgz","fileCount":17,"unpackedSize":47312,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJ3xWCRA9TVsSAnZWagAAWF4QAJNP3LE70x3ubzm0No78\nAvlNDsN6v0ScwtR9niQ1eUqjgYlDdmJGS3L2yUK1FqmwkGkcrZVwG/YR6HHw\n7fQOh0hoKmAqummgNaPtxny35SmfkQQ82wzZhVPYkUt6O22G0xE5Q8Fr7rGu\nYBNQDdMbjJ1zayHqDJzSBIWsjEKYV9EIlHLLKbgI7UaJVVmAFHxvpzTDNBq9\nA0SYe0ICwU2Pgp6U2hj//5FjCDK8zY9kTOhciMRelJRovdTwZ0XT0mNREKfp\nSjdwqeYl7M6m5BheMPKK5+GbBWf1LQ36uXtEvKrshwMnuCG6342eRVjxR1Be\n1fvarAWUfWPLgBtY9odYpEJQef6GlT1nYcDMV5mRvIOqTnaMtgS/519Gj+xb\nEElM898sh2/doc3d0m6vTsJOVP80iL9hMx1Tb4/XcxPWMcc1Ft6rRn5HvRDV\nqu8rwefgGwH6lx/D31BqsBn0JES4YkYYronEjKwtXxBJBvlQ6ZN2DJVJjkiN\neHU/faeq3CsEzKWMaRheh4wp4VkfkY4e9AxVIv71NxPu/HOOCDdmE1udPl29\naaBMeFeLk9g//+nFEpTw/js5u8zXvMjYCbGNYlhOCOg4EDendJFJKngPWSfo\nN8kPOP/2aHt+92JOstMxcsu8PFQZZjq6iAXI2V9z5/YTJvYMpcJw/LTvkCV2\nKpsG\r\n=T65U\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCoxE2/qc617Hkp+jrM3rhcATqY5CcaN1/KXl5b450CiQIhANZgTfGzglM01Xg5aPl3SfoI1OYU/iFwSJhUlFNx1g2l"}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.22-beta.4_1562868821936_0.7042076556331212"},"_hasShrinkwrap":false},"0.0.23":{"name":"@prftesp/cle-logger","version":"0.0.23","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@0.0.23","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-H0J79yPn30sR+oIasoTVfpZ3KGEWh50XFmFUbvc5EgtInAHHqUKix0GeTmCKU7WpY685v93/E2Zizfm129/h2A==","shasum":"26a314af8293b7bc5c3311ce81650ec2cc21115a","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.23.tgz","fileCount":17,"unpackedSize":47305,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJ3zsCRA9TVsSAnZWagAA0GwP/2aGWET2RWz0vuFkEqxV\nLsqzInpC9HDqWLQkASB16wBCMtQEpBqt9ykfjbiCWpPz5VTXDChInaHWks2Y\n5yjZuAQvn7nRm00KedcyHsMwbCwNZ190hl5Laz0/Qso2KVa2T/y8CgEHeWtg\nXJ5sGXruAJDLgvRPjL69YpUfkBQWQ9/imVVAIkmd78znkztVY2v+l4ltqe1r\nb5hYnN0T47XRJHEa+BgThCSRL0r+rtazQheqThUZaEnCxpxGfdaqpvMMplNd\nd+px7Az6y7kJhTwMtC+XOgGSYQRGxOvm4z+IK4hooyi0dLDgttVobPrLJPGj\nVzj/+zGd35dmIHDwfby5Pa9+A79TIhlAa3GmUvQ4pZphgUqamD4OMtksedfF\neH+Xhg9DRnJ5pDlBc1jvuY3BrfGyJKvHpsiuxJ70glyuxOlCP5qAou+WsRum\noYMMyE4Y5SaUsmWEfP2LBEePuts9RfFM58r88cwXKaoRCLHUvBxCG46MA8S+\nbmxAMx4506Pj1aUBKXCk5SiNnUAgeg9Ly7mP3ZLDV7FSv0Ld75KwNsBn8ocf\nONtxFCaPjmd5c6dIaDzSuT+WBN5lP9f0MpavHIHpuPvJ6CsTRLVI/nlEaOCZ\nGh4yYeE5rQpQaIayYuDopRgBeIIzb+E/M/HFaklWjvykOBAnRNblCf66r5Yn\nUg/L\r\n=Cezt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFS0e/NnPxQ3mc7XXAlAopIYon9tzg4nFpQ7sNAW/CJ5AiEA1fsrmZK1WQl/tNz9F2VjURdPcODsiOVfNuHDOhYWVzY="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.23_1562868971335_0.4536176560950713"},"_hasShrinkwrap":false},"0.0.24-beta.20190711.1":{"name":"@prftesp/cle-logger","version":"0.0.24-beta.20190711.1","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿CLE Logger\n===\n\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\n\n## Documentation\n\n- [Target Environment](#target-environment)\n- [Simple Example](#simple-example)\n- [Installation](#installation)\n- [Features](#features)\n- [Configuration](#configuration)\n- [API](#api)\n\t- [Logging levels](#logging-levels)\n\t- [Logging parameters](#logging-parameters)\n\t- [Available component names](#available-component-names)\n\t- [Available custom property keys](#available-custom-property-keys)\n\t- [Log codes](#log-codes)\n\t- [Registering Twilio Actions](#registering-twilio-actions)\n\t- [Version](#version)\n- [Log Object](#log-object)\n- [Advanced Configuration](#advanced-configuration)\n\t- [Log sinks](#log-sinks)\n\t- [Remote logging](#remote-logging)\n- [Changelog](#changelog)\n\n## Target Environment\n\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\n\n## Simple Example\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n// will log message to console\ncleLogger.info(\"log message\");\n```\n\n## Installation\n\n```bash\n$ npm install @prftesp/cle-logger\n```\n\n## Features\n\n- Runs in Node.js and the browser\n- Remote logging\n- Multiple custom log sinks\n- Remote minimum log level overrides\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\n- Support for tagging logs with custom component names or correlation tokens\n\n## Configuration\n\nThe logger can be used without configuration, and will by default log to console with a minimum logging level set to ```VERBOSE```.\n\nTo configure the logger call the ```init``` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\n\n**Available options**\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// Twilio account sid, required for use with CLE\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\n\n\t// Will be associated with all logs,\n\t// if not provided, a unique uuid will be generated\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\n\n\t// An array of custom log sinks that will receive an array of log objects,\n\t// and can optionally return a promise\n\tcustomLogSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: logs => console.log(logs),\n\t}],\n\n\t// A way to suppress error messages if set to false, defaults to true\n\tlogErrorsToConsole: true,\n\n\t// To support remote logging, add a url to receive logs\n\tlogUrl: \"https://example.com/api/logs\",\n\n\t// Will not send logs to the default remote log sink configured with 'logUrl'\n\tdisableRemoteLogging: false,\n\n\t// If logging Twilio Flex Actions, provide a list of actions \n\t// to be excluded from logs\n\texcludedFlexActionNames: [\"SendTyping\"],\n\n\t// Add console as a log sink, useful for debugging, defaults to true\n\tlogToConsole: true,\n\n\t// Provide a min log level, defaults to INFO\n\t// Use cleLogger.logLevels for predefined constants\n\tminLogLevel: cleLogger.logLevels.INFO,\n\n\t// A custom endpoint to fetch log level overrides\n\t// NOTE: the provided url should always end with 'accountSid=',\n\t// the library will append the accountSid\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\n\n\t// An instance of ('@twilio/flex-ui').Manager\n\ttwilioFlexManager: manager,\n});\n```\n\n## API\n\nThe logger can be imported as an object in CommonJs or ES6 syntax:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n// or\nconst cleLogger = require(\"@prftesp/cle-logger\");\n```\n\nAlternatively, all top level members of the ```cleLogger``` are available as named exports and can be imported separately:\n\n```js\nimport { info, componentNames } from \"@prftesp/cle-logger\";\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\n\n// vs\n\nimport { cleLogger } from \"@prftesp/cle-logger\";\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\n```\n\n### Logging levels\n\nLogging level constants can be found in ```cleLogger.logLevels```.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.logLevels.VERBOSE; // verbose\ncleLogger.logLevels.INFO; // info\ncleLogger.logLevels.WARN; // warn\ncleLogger.logLevels.ERROR; // error\ncleLogger.logLevels.CRITICAL; // critical\n```\n\nThere are functions available for each supported logging level:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(...);\ncleLogger.info(...);\ncleLogger.warn(...);\ncleLogger.error(...);\ncleLogger.critical(...);\n```\n\nThe functions are all async, return ```Promise<void>```, and can be optionally awaited. The suggested approach is to not await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is to suggested to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\n\n### Logging parameters\n\nAll logging functions take the same parameters:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(\n\t// The only required property, no logs will be generated without it\n\t\"Log message\",\n\n\t// Any additional log data you would like to include with the log,\n\t// can be any data type\n\t{ data: \"test\" },\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\t// CLE uses it to index these properties in Application Insights\n\t// For CLE, try to use keys found in 'cleLogger.customPropertyKeys'\n\t{ userId: 123 },\n\n\t// Another useful way to 'tag' your logs for easier searching\n\t// Can be any string, but for CLE, try to use available options in 'cleLogger.componentNames'\n\t// If you would like more component names added, please let us know \n\t\"ProfileComponent\",\n\n\t// Log code, can be any number used to categorize logs,\n\t// there will be a separate document listing CLE codes\n\t70123,\n\n\t// Correlation token to be associated with this log only,\n\t// if one is not provided, the token supplied/generated during 'init' will be used\n\t\"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\"\n);\n```\n\n### Available component names\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.componentNames.SESSION;\ncleLogger.componentNames.DIALER;\ncleLogger.componentNames.DASHBOARD;\ncleLogger.componentNames.AFTERCALL;\ncleLogger.componentNames.SUPERVISOR;\n```\n\n### Available custom property keys\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.customPropertyKeys.CHANNEL_SID;\ncleLogger.customPropertyKeys.TASK_SID;\ncleLogger.customPropertyKeys.CALL_SID;\ncleLogger.customPropertyKeys.WORKFLOW_SID;\ncleLogger.customPropertyKeys.WORKER_SID;\n```\n\n### Log codes\n\n[TODO]\n\n### Registering Twilio Actions\n\nExample in a Twilio Flex UI plugin:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n/**\n * @param flex { typeof import('@twilio/flex-ui') }\n * @param manager { import('@twilio/flex-ui').Manager }\n */\ninit(flex, manager) {\n\tcleLogger.init({\n\t\taccountSid: \"your-account-sid\"\n\t});\n\n\tcleLogger.registerTwilioActions(flex);\n}\n```\n\n**Note**: A Twilio account sid must be provided during ```init``` with the ```accountSid``` parameter, otherwise the actions won't be logged.\n\nThe logger will loop through all actions found under ```flex.Actions.actions```, and attach ```after``` event handlers to log the action name and payload. Any private properties found in the payload (starting with '_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Any actions names configured in ```excludedFlexActionNames``` will not be included in the logs.\n\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\n\n**Twilio Actions log levels**\n\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of ```INFO```.\n\n``` js\nAcceptTask: INFO\nCancelTransfer: INFO\nCompleteTask: INFO\nHangupCall: INFO\nHideDirectory: VERBOSE\nHistoryGo: VERBOSE\nHistoryGoBack: VERBOSE\nHistoryGoForward: VERBOSE\nHistoryPush: VERBOSE\nHistoryReplace: VERBOSE\nHoldCall: INFO\nHoldParticipant: INFO\nKickParticipant: INFO\nLogout: INFO\nMonitorCall: INFO\nNavigateToView: INFO\nRejectTask: INFO\nSelectTask: INFO\nSelectTaskInSupervisor: INFO\nSelectWorkerInSupervisor: INFO\nSendMessage: INFO\nSendTyping: VERBOSE\nSetActivity: INFO\nSetInputText: VERBOSE\nShowDirectory: VERBOSE\nStopMonitoringCall: INFO\nToggleMute: INFO\nToggleSidebar: VERBOSE\nTransferTask: INFO\nUnholdCall: INFO\nUnholdParticipant: INFO\nWrapupTask: INFO\n```\n\n### Version\n\nVersion will be automatically included in all logs.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.version; // 1.0.0\n```\n\n## Log Object\n\n```js\n{\n\t// Account sid if provided during init\n\taccountSid: string;\n\n\t// Any additional log data to be included with the log\n\tadditionalData: unknown;\n\t\n\t// Component name, if provided\n\tcomponentName: string;\n\n\t// Generated or configured token\n\tcorrelationToken: string;\n\n\t// Current UTC datetime in ISO 8601 format\n\teventDate: string;\n\n\t// Log level\n\tlevel: string;\n\n\t// Log code, if provided\n\tlogCode: number;\n\n\t// Log message\n\tmessage: string;\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\tproperties: object;\n\n\t// BrowserFunction if the environment is the browser\n\t// CloudFunction if the environment is Node.js\n\tsource: string;\n\n\t// Logging library version \n\tloggerVersion: string;\n}\n```\n\n## Advanced Configuration\n\n### Log sinks\n\nCustom log sinks can be provided via the ```customLogSinks``` config. It's an array of objects containing a ```name``` and a ```log``` method. All provided log sinks, along with the built-in remote and console log sinks will be executed in parallel. You can choose to await the logs to make sure all logging operations are completed.\nThe ```log``` method will receive an array of log objects for you to handle. If you're doing async requests in your custom log sinks, it is recommended to return the Promise.\n\nExample logging to file in Node.js:\n\n``` js\nconst fs = require(\"fs\");\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\ncleLogger.init({\n\tcustomLogSinks: [{\n\t\tname: \"testFileSink\",\n\t\tlog: logs => {\n\t\t\tfs.appendFile(\n\t\t\t\t\"./log.txt\",\n\t\t\t\t`${JSON.stringify(logs)}`,\n\t\t\t\tconsole.log\n\t\t\t);\n\t\t}\n\t}]\n});\n```\n\n### Remote logging\n\nThe library provides a way to provide you own remote log url, or to remotely override the min log level. This can be useful for temporarily setting the log level to verbose in a production environment, for quick troubleshooting.\n\n**Remote log endpoint**\n\nTo implement your own remote logging endpoint, all you have to do is accept ```POST``` requests, with a JSON payload of an array of [log objects](#log-object), and return either ```{ success: true }``` or ```{ success: false }```. The endpoint should always return a ```200 OK``` status code. A good use-case would be leveraging a serverless api platform to output logs to a queue.\n\nExample Azure Function:\n\n```js\nmodule.exports = async function (context, req) {\n    context.log(req.body); // body will have the log array\n\n    context.res = {\n        status: 200,\n        body: { success: true }\n    };\n};\n```\n\n**Remote log level override**\n\nThe ```minLogLevelUrl``` configuration property is used to provide an endpoint that accepts ```GET``` requests and a ```accountSid``` query string parameter. ```accountSid``` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the ```minLogLevel``` configuration property. If null is returned, the ```minLogLevel``` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a ```200 OK``` status code and use the ```success``` property to indicate failure.\n\nExample success response:\n\n```json\n{ \n\t\"success\": true,\n\t\"minLogLevel\": \"warn\"\n}\n```\n\nExample error response:\n\n```json\n{ \n\t\"success\": false,\n\t\"minLogLevel\": null\n}\n```\n\n## Changelog\n\n### v1.0.0 [2019-11-07]\n\n- Initial release","readmeFilename":"README.md","_id":"@prftesp/cle-logger@0.0.24-beta.20190711.1","_nodeVersion":"10.16.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-aR+A2Mqbn5yPI9BCtMw6qRIxV22SVzXwLry8N1XmRl/ltLt/k07OnMpQYFZPtNsOw791gmDjeU0Umri93lAqEg==","shasum":"356c51e7157a3417cbba9ed24e5829d832120078","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.24-beta.20190711.1.tgz","fileCount":17,"unpackedSize":46394,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJ6byCRA9TVsSAnZWagAAwb8QAJepbHhqAvYi0WIDsU+J\np4UZeNBECjFDCKZTc8pp09c8SC/BUZ5oVhy2/7yAk397Wel1elwlfbdqniYY\nuPCDBnAmJtukUowHHYb8Z5ig879ocipxBidIRfuyCSiBOBV8gATLAgRDnN/c\njUMMH/gDWS1AImzmkT5Zu/8aMejrNEAidg03LFq0NxlUN/dT5OMEezM8EYhJ\nACi2LYxUAbUeqq2k+11Lt4/5WQGpBHKjVfoOci+Li/Y/qWSVBqn/yVF2s30d\nzXosRrtEAo9kaOfd/UvzWWEZFZtfxB6+DtmIXmEy6l8qroh44iYLtpEfZfYX\nT5A6zb1ZsShCP4MpzWJnY1seGYqGc+5O5xze+Ufy20z+D/eVa/ULU7+ZJIHU\nCRUqjKudhv0mP7OUT1ap0m8ULg+9sEmxVOfo2oi+nOU5Kd7ixavX+VElBdum\nyzl8xsuBcSrws1Anw3z+B6q3XDf89CsN99qN8WlmbrtTYQ7qVqRimCzdo1RM\nhxldA3wr++sDpoM8WMj00PcOC0z5HCJOwtTY1I/IvACvscIrFg6EUrT1Pn6A\n2orc1d9Vt7bLjmwXPNOCJb1raq6HrxXm8jInKUt8er+Aje8RjWN2aBbY1jiV\nv05wZDawHOFzzjS/fzQsfCU7ttoDFs6/F91g6rBgXrFzwpcS4qvp81BaYrly\n6uLA\r\n=w+36\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCpzQnjHHMrJVs5/e62YEMebYtuzxY/VxPZ5kfCKFG9ZQIgNhx1ZTOpq1ZHjdwV4D+nDyPmWRv4vLbqlgPiLpTDs+c="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.24-beta.20190711.1_1562879729373_0.2473443897652039"},"_hasShrinkwrap":false},"0.0.25-beta.20190711.2":{"name":"@prftesp/cle-logger","version":"0.0.25-beta.20190711.2","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿CLE Logger\n===\n\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\n\n## Documentation\n\n- [Target Environment](#target-environment)\n- [Simple Example](#simple-example)\n- [Installation](#installation)\n- [Features](#features)\n- [Configuration](#configuration)\n- [API](#api)\n\t- [Logging levels](#logging-levels)\n\t- [Logging parameters](#logging-parameters)\n\t- [Available component names](#available-component-names)\n\t- [Available custom property keys](#available-custom-property-keys)\n\t- [Log codes](#log-codes)\n\t- [Registering Twilio Actions](#registering-twilio-actions)\n\t- [Version](#version)\n- [Log Object](#log-object)\n- [Advanced Configuration](#advanced-configuration)\n\t- [Log sinks](#log-sinks)\n\t- [Remote logging](#remote-logging)\n- [Changelog](#changelog)\n\n## Target Environment\n\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\n\n## Simple Example\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n// will log message to console\ncleLogger.info(\"log message\");\n```\n\n## Installation\n\n```bash\n$ npm install @prftesp/cle-logger\n```\n\n## Features\n\n- Runs in Node.js and the browser\n- Remote logging\n- Multiple custom log sinks\n- Remote minimum log level overrides\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\n- Support for tagging logs with custom component names or correlation tokens\n\n## Configuration\n\nThe logger can be used without configuration, and will by default log to console with a minimum logging level set to ```VERBOSE```.\n\nTo configure the logger call the ```init``` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\n\n**Available options**\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// Twilio account sid, required for use with CLE\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\n\n\t// Will be associated with all logs,\n\t// if not provided, a unique uuid will be generated\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\n\n\t// An array of custom log sinks that will receive an array of log objects,\n\t// and can optionally return a promise\n\tcustomLogSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: logs => console.log(logs),\n\t}],\n\n\t// A way to suppress error messages if set to false, defaults to true\n\tlogErrorsToConsole: true,\n\n\t// To support remote logging, add a url to receive logs\n\tlogUrl: \"https://example.com/api/logs\",\n\n\t// Will not send logs to the default remote log sink configured with 'logUrl'\n\tdisableRemoteLogging: false,\n\n\t// If logging Twilio Flex Actions, provide a list of actions \n\t// to be excluded from logs\n\texcludedFlexActionNames: [\"SendTyping\"],\n\n\t// Add console as a log sink, useful for debugging, defaults to true\n\tlogToConsole: true,\n\n\t// Provide a min log level, defaults to INFO\n\t// Use cleLogger.logLevels for predefined constants\n\tminLogLevel: cleLogger.logLevels.INFO,\n\n\t// A custom endpoint to fetch log level overrides\n\t// NOTE: the provided url should always end with 'accountSid=',\n\t// the library will append the accountSid\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\n\n\t// An instance of ('@twilio/flex-ui').Manager\n\ttwilioFlexManager: manager,\n});\n```\n\n## API\n\nThe logger can be imported as an object in CommonJs or ES6 syntax:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n// or\nconst cleLogger = require(\"@prftesp/cle-logger\");\n```\n\nAlternatively, all top level members of the ```cleLogger``` are available as named exports and can be imported separately:\n\n```js\nimport { info, componentNames } from \"@prftesp/cle-logger\";\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\n\n// vs\n\nimport { cleLogger } from \"@prftesp/cle-logger\";\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\n```\n\n### Logging levels\n\nLogging level constants can be found in ```cleLogger.logLevels```.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.logLevels.VERBOSE; // verbose\ncleLogger.logLevels.INFO; // info\ncleLogger.logLevels.WARN; // warn\ncleLogger.logLevels.ERROR; // error\ncleLogger.logLevels.CRITICAL; // critical\n```\n\nThere are functions available for each supported logging level:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(...);\ncleLogger.info(...);\ncleLogger.warn(...);\ncleLogger.error(...);\ncleLogger.critical(...);\n```\n\nThe functions are all async, return ```Promise<void>```, and can be optionally awaited. The suggested approach is to not await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is to suggested to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\n\n### Logging parameters\n\nAll logging functions take the same parameters:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(\n\t// The only required property, no logs will be generated without it\n\t\"Log message\",\n\n\t// Any additional log data you would like to include with the log,\n\t// can be any data type\n\t{ data: \"test\" },\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\t// CLE uses it to index these properties in Application Insights\n\t// For CLE, try to use keys found in 'cleLogger.customPropertyKeys'\n\t{ userId: 123 },\n\n\t// Another useful way to 'tag' your logs for easier searching\n\t// Can be any string, but for CLE, try to use available options in 'cleLogger.componentNames'\n\t// If you would like more component names added, please let us know \n\t\"ProfileComponent\",\n\n\t// Log code, can be any number used to categorize logs,\n\t// there will be a separate document listing CLE codes\n\t70123,\n\n\t// Correlation token to be associated with this log only,\n\t// if one is not provided, the token supplied/generated during 'init' will be used\n\t\"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\"\n);\n```\n\n### Available component names\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.componentNames.SESSION;\ncleLogger.componentNames.DIALER;\ncleLogger.componentNames.DASHBOARD;\ncleLogger.componentNames.AFTERCALL;\ncleLogger.componentNames.SUPERVISOR;\n```\n\n### Available custom property keys\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.customPropertyKeys.CHANNEL_SID;\ncleLogger.customPropertyKeys.TASK_SID;\ncleLogger.customPropertyKeys.CALL_SID;\ncleLogger.customPropertyKeys.WORKFLOW_SID;\ncleLogger.customPropertyKeys.WORKER_SID;\n```\n\n### Log codes\n\n[TODO]\n\n### Registering Twilio Actions\n\nExample in a Twilio Flex UI plugin:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n/**\n * @param flex { typeof import('@twilio/flex-ui') }\n * @param manager { import('@twilio/flex-ui').Manager }\n */\ninit(flex, manager) {\n\tcleLogger.init({\n\t\taccountSid: \"your-account-sid\"\n\t});\n\n\tcleLogger.registerTwilioActions(flex);\n}\n```\n\n**Note**: A Twilio account sid must be provided during ```init``` with the ```accountSid``` parameter, otherwise the actions won't be logged.\n\nThe logger will loop through all actions found under ```flex.Actions.actions```, and attach ```after``` event handlers to log the action name and payload. Any private properties found in the payload (starting with '_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Any actions names configured in ```excludedFlexActionNames``` will not be included in the logs.\n\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\n\n**Twilio Actions log levels**\n\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of ```INFO```.\n\n``` js\nAcceptTask: INFO\nCancelTransfer: INFO\nCompleteTask: INFO\nHangupCall: INFO\nHideDirectory: VERBOSE\nHistoryGo: VERBOSE\nHistoryGoBack: VERBOSE\nHistoryGoForward: VERBOSE\nHistoryPush: VERBOSE\nHistoryReplace: VERBOSE\nHoldCall: INFO\nHoldParticipant: INFO\nKickParticipant: INFO\nLogout: INFO\nMonitorCall: INFO\nNavigateToView: INFO\nRejectTask: INFO\nSelectTask: INFO\nSelectTaskInSupervisor: INFO\nSelectWorkerInSupervisor: INFO\nSendMessage: INFO\nSendTyping: VERBOSE\nSetActivity: INFO\nSetInputText: VERBOSE\nShowDirectory: VERBOSE\nStopMonitoringCall: INFO\nToggleMute: INFO\nToggleSidebar: VERBOSE\nTransferTask: INFO\nUnholdCall: INFO\nUnholdParticipant: INFO\nWrapupTask: INFO\n```\n\n### Version\n\nVersion will be automatically included in all logs.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.version; // 1.0.0\n```\n\n## Log Object\n\n```js\n{\n\t// Account sid if provided during init\n\taccountSid: string;\n\n\t// Any additional log data to be included with the log\n\tadditionalData: unknown;\n\t\n\t// Component name, if provided\n\tcomponentName: string;\n\n\t// Generated or configured token\n\tcorrelationToken: string;\n\n\t// Current UTC datetime in ISO 8601 format\n\teventDate: string;\n\n\t// Log level\n\tlevel: string;\n\n\t// Log code, if provided\n\tlogCode: number;\n\n\t// Log message\n\tmessage: string;\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\tproperties: object;\n\n\t// BrowserFunction if the environment is the browser\n\t// CloudFunction if the environment is Node.js\n\tsource: string;\n\n\t// Logging library version \n\tloggerVersion: string;\n}\n```\n\n## Advanced Configuration\n\n### Log sinks\n\nCustom log sinks can be provided via the ```customLogSinks``` config. It's an array of objects containing a ```name``` and a ```log``` method. All provided log sinks, along with the built-in remote and console log sinks will be executed in parallel. You can choose to await the logs to make sure all logging operations are completed.\nThe ```log``` method will receive an array of log objects for you to handle. If you're doing async requests in your custom log sinks, it is recommended to return the Promise.\n\nExample logging to file in Node.js:\n\n``` js\nconst fs = require(\"fs\");\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\ncleLogger.init({\n\tcustomLogSinks: [{\n\t\tname: \"testFileSink\",\n\t\tlog: logs => {\n\t\t\tfs.appendFile(\n\t\t\t\t\"./log.txt\",\n\t\t\t\t`${JSON.stringify(logs)}`,\n\t\t\t\tconsole.log\n\t\t\t);\n\t\t}\n\t}]\n});\n```\n\n### Remote logging\n\nThe library provides a way to provide you own remote log url, or to remotely override the min log level. This can be useful for temporarily setting the log level to verbose in a production environment, for quick troubleshooting.\n\n**Remote log endpoint**\n\nTo implement your own remote logging endpoint, all you have to do is accept ```POST``` requests, with a JSON payload of an array of [log objects](#log-object), and return either ```{ success: true }``` or ```{ success: false }```. The endpoint should always return a ```200 OK``` status code. A good use-case would be leveraging a serverless api platform to output logs to a queue.\n\nExample Azure Function:\n\n```js\nmodule.exports = async function (context, req) {\n    context.log(req.body); // body will have the log array\n\n    context.res = {\n        status: 200,\n        body: { success: true }\n    };\n};\n```\n\n**Remote log level override**\n\nThe ```minLogLevelUrl``` configuration property is used to provide an endpoint that accepts ```GET``` requests and a ```accountSid``` query string parameter. ```accountSid``` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the ```minLogLevel``` configuration property. If null is returned, the ```minLogLevel``` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a ```200 OK``` status code and use the ```success``` property to indicate failure.\n\nExample success response:\n\n```json\n{ \n\t\"success\": true,\n\t\"minLogLevel\": \"warn\"\n}\n```\n\nExample error response:\n\n```json\n{ \n\t\"success\": false,\n\t\"minLogLevel\": null\n}\n```\n\n## Changelog\n\n### v1.0.0 [2019-11-07]\n\n- Initial release","readmeFilename":"README.md","_id":"@prftesp/cle-logger@0.0.25-beta.20190711.2","_nodeVersion":"10.16.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-vbxRwTwq4McYG/Ar+FyYcx0JpXBbtJQ96nmMKOqAyJ+1cx6wvBkqxpfEEtzC869ecRsxD2xPjYEmctm9Wi06rA==","shasum":"372e0b301a25eecb49032e5a003ad6aa67e6ab83","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.25-beta.20190711.2.tgz","fileCount":17,"unpackedSize":46394,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJ+MaCRA9TVsSAnZWagAAD9kP/02dgVBFYw+2wtsPtj/f\nUNECNZhndhDrnBxEPbjIYXGkVr8lzjp7tRPtMIyimQhORsfAnfnTt03PMMOz\nMr0VYXcRAv3dQB7OKjAHEDJV263owPNp642fqs9QrPwWpIUHnJVtWftzHIIn\nM+9oQKUVW8sPsRQO5CDahQB0NXwT5oGGB1Ko5Tjucb/blZbuVKBniX5LnefD\n9sN3DM4ZWoFp3tLf+7uuiWZLcOrh8YsEJpte4fB+casEH0ZCfRs+uXO1W+Xl\nMctGsKKJRJIu8XRtnpbjB2uF93IU+PIwhGtblBI4susqqrnRZ9/he0XnwwVR\nsKlUJHvf0r6m3D6trECzomZ7eZH1XR26mSp+I9HvgsZOveDyAiOKXRX6JUxL\nx+ylTdAY9EhOyDtVj0mKExyAILVI9fC218EEOiokJbQ7eaEZGeXOdk0L2fIe\nMcWzWGrgMl4T5KPd2jWZZi7RMsbB7LJelam+lrT0O1ZlxYcRQz95r5H3xILR\nS65hqlXE2Ihsp/6obC44347grl+F+ILol8DGLFi8jc8wEybSO94/HN4uyPVI\nT7wugdDWBTRsz97xYb0guC2ZPy0QFD3WaHQEri4p9kvS2Yhe/vMTdMzCubDt\nbGc0Mld2tyXMngeIY/5JXTsZlYnwixJVBhQPG838ClaKZkN/W4DskiZb+r9W\nUTHL\r\n=dXlB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIC9fXZ81JmS2s5LPZkNQBEXXuk8UBvT6m1gIIDZNibyBAiADxMPoZC3u8AQVtP62SLm/gOlWESDp0vmlY21VRwjnYA=="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.25-beta.20190711.2_1562895129723_0.5833911412320829"},"_hasShrinkwrap":false},"0.0.25":{"name":"@prftesp/cle-logger","version":"0.0.25","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@0.0.25","_nodeVersion":"10.16.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-Ye2bo7IsanaL5AaBN9pfj+IYUg1IB0zqQ3Wgzzfw51C5iejQR9M9w0UtdS807FQ7Nw8oOZ2h9qcVro1nCoKsZA==","shasum":"37349ddeef337a113c209433aff85bd4c757052e","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.25.tgz","fileCount":17,"unpackedSize":46295,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJ+OkCRA9TVsSAnZWagAAdqkQAJOwvoqVypH7oLtUf8v1\nYf+g1RyIAc4DlDA76jV3sLT+rX1HePc1fHhxM2oAX0mq2SicmSCjQcrRQ5JV\nCekC5hqboE+4eyaO03mXvy3Bjn2qMO75aDOl0sPcYSXdSY5DL6zXelm5kcAZ\nreMkFlmOV4wW3I8Llz4VgF013QcKoxoPpEYUGZo4L+p8hWg9EX1oLXliwjWx\nitLxmDRy4X9SVt8npqcqFVgsfvYsW4WGyY58cJll6CILHz7tNzQkvxZ2m2Cg\nvN6q6hbkHhdC0eDh9NHJqrkzmZGSxeHt0Fy6E9l1+nRidnZnV3JXpw1AUzyO\n0Kj74bmAyBg14/L1XOAFfwAXOoVgsJQbjCjLCcPwi+CRL3QNoSCYshXPTmeX\neg/1SVJl5jUOx+Xm506nqfhiGH1LhhFBgbqEMsHM3Jr2dCKT8tTKROKPd4tW\n8ZA+8RjMUuxFyXSFG1VSBp63RTTCyz8VKdCk1LdofCDWyLfCOeNe1ogxGpf5\nhOybl0aL5V27wE+0eFrQ+ol6HGpmKnr62ICUaHa+cdVc0stZniGrBZgN03og\nOsBj0Ar6U+w0kxwBDb8AO6ba5ambEI1r9QEp3wBRShX3LOv/3ZyNKXYotJxA\nEOoSW16YoTKTsy8JOD5SXYFBsIStdwIDamV8EVPtoudwoBBZOouxvdfiuimz\nWUg0\r\n=tCIL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQChS10lCWsg4pRF7rcuy3t0xh37M88b/0sk1u3p6eJf9AIhANpzLjIxipqxeumgR7rG+doabmVzbFYxZKPbi5OV3i6x"}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.25_1562895267336_0.18230696194667928"},"_hasShrinkwrap":false},"0.0.26-beta.20190712.2":{"name":"@prftesp/cle-logger","version":"0.0.26-beta.20190712.2","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿CLE Logger\n===\n\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\n\n## Documentation\n\n- [Target Environment](#target-environment)\n- [Simple Example](#simple-example)\n- [Installation](#installation)\n- [Features](#features)\n- [Configuration](#configuration)\n- [API](#api)\n\t- [Logging levels](#logging-levels)\n\t- [Logging parameters](#logging-parameters)\n\t- [Available component names](#available-component-names)\n\t- [Available custom property keys](#available-custom-property-keys)\n\t- [Log codes](#log-codes)\n\t- [Registering Twilio Actions](#registering-twilio-actions)\n\t- [Version](#version)\n- [Log Object](#log-object)\n- [Advanced Configuration](#advanced-configuration)\n\t- [Log sinks](#log-sinks)\n\t- [Remote logging](#remote-logging)\n- [Changelog](#changelog)\n\n## Target Environment\n\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\n\n## Simple Example\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n// will log message to console\ncleLogger.info(\"log message\");\n```\n\n## Installation\n\n```bash\n$ npm install @prftesp/cle-logger\n```\n\n## Features\n\n- Runs in Node.js and the browser\n- Remote logging\n- Multiple custom log sinks\n- Remote minimum log level overrides\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\n- Support for tagging logs with custom component names or correlation tokens\n\n## Configuration\n\nThe logger can be used without configuration, and will by default log to console with a minimum logging level set to ```VERBOSE```.\n\nTo configure the logger call the ```init``` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\n\n**Available options**\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// Twilio account sid, required for use with CLE\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\n\n\t// Will be associated with all logs,\n\t// if not provided, a unique uuid will be generated\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\n\n\t// An array of custom log sinks that will receive an array of log objects,\n\t// and can optionally return a promise\n\tcustomLogSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: logs => console.log(logs),\n\t}],\n\n\t// A way to suppress error messages if set to false, defaults to true\n\tlogErrorsToConsole: true,\n\n\t// To support remote logging, add a url to receive logs\n\tlogUrl: \"https://example.com/api/logs\",\n\n\t// Will not send logs to the default remote log sink configured with 'logUrl'\n\tdisableRemoteLogging: false,\n\n\t// If logging Twilio Flex Actions, provide a list of actions \n\t// to be excluded from logs\n\texcludedFlexActionNames: [\"SendTyping\"],\n\n\t// Add console as a log sink, useful for debugging, defaults to true\n\tlogToConsole: true,\n\n\t// Provide a min log level, defaults to INFO\n\t// Use cleLogger.logLevels for predefined constants\n\tminLogLevel: cleLogger.logLevels.INFO,\n\n\t// A custom endpoint to fetch log level overrides\n\t// NOTE: the provided url should always end with 'accountSid=',\n\t// the library will append the accountSid\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\n\n\t// An instance of ('@twilio/flex-ui').Manager\n\ttwilioFlexManager: manager,\n});\n```\n\n## API\n\nThe logger can be imported as an object in CommonJs or ES6 syntax:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n// or\nconst cleLogger = require(\"@prftesp/cle-logger\");\n```\n\nAlternatively, all top level members of the ```cleLogger``` are available as named exports and can be imported separately:\n\n```js\nimport { info, componentNames } from \"@prftesp/cle-logger\";\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\n\n// vs\n\nimport { cleLogger } from \"@prftesp/cle-logger\";\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\n```\n\n### Logging levels\n\nLogging level constants can be found in ```cleLogger.logLevels```.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.logLevels.VERBOSE; // verbose\ncleLogger.logLevels.INFO; // info\ncleLogger.logLevels.WARN; // warn\ncleLogger.logLevels.ERROR; // error\ncleLogger.logLevels.CRITICAL; // critical\n```\n\nThere are functions available for each supported logging level:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(...);\ncleLogger.info(...);\ncleLogger.warn(...);\ncleLogger.error(...);\ncleLogger.critical(...);\n```\n\nThe functions are all async, return ```Promise<void>```, and can be optionally awaited. The suggested approach is to not await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is to suggested to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\n\n### Logging parameters\n\nAll logging functions take the same parameters:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(\n\t// The only required property, no logs will be generated without it\n\t\"Log message\",\n\n\t// Any additional log data you would like to include with the log,\n\t// can be any data type\n\t{ data: \"test\" },\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\t// CLE uses it to index these properties in Application Insights\n\t// For CLE, try to use keys found in 'cleLogger.customPropertyKeys'\n\t{ userId: 123 },\n\n\t// Another useful way to 'tag' your logs for easier searching\n\t// Can be any string, but for CLE, try to use available options in 'cleLogger.componentNames'\n\t// If you would like more component names added, please let us know \n\t\"ProfileComponent\",\n\n\t// Log code, can be any number used to categorize logs,\n\t// there will be a separate document listing CLE codes\n\t70123,\n\n\t// Correlation token to be associated with this log only,\n\t// if one is not provided, the token supplied/generated during 'init' will be used\n\t\"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\"\n);\n```\n\n### Available component names\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.componentNames.SESSION;\ncleLogger.componentNames.DIALER;\ncleLogger.componentNames.DASHBOARD;\ncleLogger.componentNames.AFTERCALL;\ncleLogger.componentNames.SUPERVISOR;\n```\n\n### Available custom property keys\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.customPropertyKeys.CHANNEL_SID;\ncleLogger.customPropertyKeys.TASK_SID;\ncleLogger.customPropertyKeys.CALL_SID;\ncleLogger.customPropertyKeys.WORKFLOW_SID;\ncleLogger.customPropertyKeys.WORKER_SID;\n```\n\n### Log codes\n\n[TODO]\n\n### Registering Twilio Actions\n\nExample in a Twilio Flex UI plugin:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n/**\n * @param flex { typeof import('@twilio/flex-ui') }\n * @param manager { import('@twilio/flex-ui').Manager }\n */\ninit(flex, manager) {\n\tcleLogger.init({\n\t\taccountSid: \"your-account-sid\"\n\t});\n\n\tcleLogger.registerTwilioActions(flex);\n}\n```\n\n**Note**: A Twilio account sid must be provided during ```init``` with the ```accountSid``` parameter, otherwise the actions won't be logged.\n\nThe logger will loop through all actions found under ```flex.Actions.actions```, and attach ```after``` event handlers to log the action name and payload. Any private properties found in the payload (starting with '_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Any actions names configured in ```excludedFlexActionNames``` will not be included in the logs.\n\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\n\n**Twilio Actions log levels**\n\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of ```INFO```.\n\n``` js\nAcceptTask: INFO\nCancelTransfer: INFO\nCompleteTask: INFO\nHangupCall: INFO\nHideDirectory: VERBOSE\nHistoryGo: VERBOSE\nHistoryGoBack: VERBOSE\nHistoryGoForward: VERBOSE\nHistoryPush: VERBOSE\nHistoryReplace: VERBOSE\nHoldCall: INFO\nHoldParticipant: INFO\nKickParticipant: INFO\nLogout: INFO\nMonitorCall: INFO\nNavigateToView: INFO\nRejectTask: INFO\nSelectTask: INFO\nSelectTaskInSupervisor: INFO\nSelectWorkerInSupervisor: INFO\nSendMessage: INFO\nSendTyping: VERBOSE\nSetActivity: INFO\nSetInputText: VERBOSE\nShowDirectory: VERBOSE\nStopMonitoringCall: INFO\nToggleMute: INFO\nToggleSidebar: VERBOSE\nTransferTask: INFO\nUnholdCall: INFO\nUnholdParticipant: INFO\nWrapupTask: INFO\n```\n\n### Version\n\nVersion will be automatically included in all logs.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.version; // 1.0.0\n```\n\n## Log Object\n\n```js\n{\n\t// Account sid if provided during init\n\taccountSid: string;\n\n\t// Any additional log data to be included with the log\n\tadditionalData: unknown;\n\t\n\t// Component name, if provided\n\tcomponentName: string;\n\n\t// Generated or configured token\n\tcorrelationToken: string;\n\n\t// Current UTC datetime in ISO 8601 format\n\teventDate: string;\n\n\t// Log level\n\tlevel: string;\n\n\t// Log code, if provided\n\tlogCode: number;\n\n\t// Log message\n\tmessage: string;\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\tproperties: object;\n\n\t// BrowserFunction if the environment is the browser\n\t// CloudFunction if the environment is Node.js\n\tsource: string;\n\n\t// Logging library version \n\tloggerVersion: string;\n}\n```\n\n## Advanced Configuration\n\n### Log sinks\n\nCustom log sinks can be provided via the ```customLogSinks``` config. It's an array of objects containing a ```name``` and a ```log``` method. All provided log sinks, along with the built-in remote and console log sinks will be executed in parallel. You can choose to await the logs to make sure all logging operations are completed.\nThe ```log``` method will receive an array of log objects for you to handle. If you're doing async requests in your custom log sinks, it is recommended to return the Promise.\n\nExample logging to file in Node.js:\n\n``` js\nconst fs = require(\"fs\");\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\ncleLogger.init({\n\tcustomLogSinks: [{\n\t\tname: \"testFileSink\",\n\t\tlog: logs => {\n\t\t\tfs.appendFile(\n\t\t\t\t\"./log.txt\",\n\t\t\t\t`${JSON.stringify(logs)}`,\n\t\t\t\tconsole.log\n\t\t\t);\n\t\t}\n\t}]\n});\n```\n\n### Remote logging\n\nThe library provides a way to provide you own remote log url, or to remotely override the min log level. This can be useful for temporarily setting the log level to verbose in a production environment, for quick troubleshooting.\n\n**Remote log endpoint**\n\nTo implement your own remote logging endpoint, all you have to do is accept ```POST``` requests, with a JSON payload of an array of [log objects](#log-object), and return either ```{ success: true }``` or ```{ success: false }```. The endpoint should always return a ```200 OK``` status code. A good use-case would be leveraging a serverless api platform to output logs to a queue.\n\nExample Azure Function:\n\n```js\nmodule.exports = async function (context, req) {\n    context.log(req.body); // body will have the log array\n\n    context.res = {\n        status: 200,\n        body: { success: true }\n    };\n};\n```\n\n**Remote log level override**\n\nThe ```minLogLevelUrl``` configuration property is used to provide an endpoint that accepts ```GET``` requests and a ```accountSid``` query string parameter. ```accountSid``` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the ```minLogLevel``` configuration property. If null is returned, the ```minLogLevel``` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a ```200 OK``` status code and use the ```success``` property to indicate failure.\n\nExample success response:\n\n```json\n{ \n\t\"success\": true,\n\t\"minLogLevel\": \"warn\"\n}\n```\n\nExample error response:\n\n```json\n{ \n\t\"success\": false,\n\t\"minLogLevel\": null\n}\n```\n\n## Changelog\n\n### v1.0.0 [2019-11-07]\n\n- Initial release","readmeFilename":"README.md","_id":"@prftesp/cle-logger@0.0.26-beta.20190712.2","_nodeVersion":"10.16.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-5+WrPjjpk24yikQv5dB8QWF1Otlf8hprTuPGLjmeheS1+yflf+Maz/6nUVyK8Y1SQJLJDkxuY8LhfAKYSzkxJQ==","shasum":"b52320b398ab0b49227c157c15d05ddf852c1989","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.26-beta.20190712.2.tgz","fileCount":17,"unpackedSize":46394,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdKJJZCRA9TVsSAnZWagAA0IIP/iCIFjrRRiDcbbTR/uMb\nJ3nIBCUfX7x6foAH4ggU+6DKgcE3gp07TFC38X0xlem1YutRwqydnRbilBZS\n9FJZEIBzIQFrx5TlajideLnRbjaomrT+K6LCCdv4IryBv90N6q0Ib4JLFqFi\nXwdgvcjZDlWVv4sr5fW7v6qHKAcZjjT9V0MDFfHC/1c6RnSYnpYR/HYxfoh6\nSEgewZKVtNGbBY/ufGxgGwLyifd+Vl8jE3zMByCWEbvRe1Cgq4VaDrl5qXgt\nXhC969OBFwlsmUyQDrhnSJOgZmyBRNb6HbED+t9msjlPdEccDjZQFjWC+cgz\njH4aCDC7Nplo5Zm/93Lci9jVE9B6r9zyDrKx8hidJuv3ftF5AENvjDj9Imvm\nLeW7aUaUWt66BAmrnSFweLoJjTOEhJFEWVyEzznINHuvgbewonlyVyW/cd+1\nhUVj3ShRSDEJL7YmuvnqewklvlnpXfFcUuGJSVj5pnTVmcMeOzh3Pjo9NAZo\nhIEehJMcZSomHcXy03LtlcLdXtB0EiivIO+u/sBmx+2bfq067bOiV/VS9nNU\nY4iLxCq6AClXaajod8TyrAaILSplQmU/UG4+rqLxMOgSZDLTVjmbcyEVbAiT\nAR/JEga4OkrsrJqqM9sI7ZCvUsOvwEiqxZRvKPbE1eF4LsL7GT2s9e796QbQ\nOzqQ\r\n=/GlE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDJcVFnikN3WKf1jFxW+cpaWtujmfjpaEYr+/evc3OncwIhAJzkF2TAyx6tpnit54PjOrpTuoaPJGJPoZT1T92lzYVf"}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.26-beta.20190712.2_1562939992029_0.14569918465008524"},"_hasShrinkwrap":false},"0.0.26":{"name":"@prftesp/cle-logger","version":"0.0.26","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@0.0.26","_nodeVersion":"10.16.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-EaKLBg9iDvRcoWXJo60gPONDOVvVJMDyX2dcnjh52St3rnIOnwD2h3v/YnX6wpV+J7wQKE1EnNdSAXdY/4UVvQ==","shasum":"ecf336982acf48db2f2747a6d73716097e4af053","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.26.tgz","fileCount":17,"unpackedSize":46295,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdKJLrCRA9TVsSAnZWagAAcPgP/RpwseDMvyznaL8uf+kN\nprpWsCq6IahIYU4Y0AqmRuQBw/lmIES4WFPjNodP4oXHGyOFQwM8ftLo/hti\nKEoGSF5NxNJ9SaDZKNt4mibKTBZrZERj0aYn1WBbtUrFd4VMf45bmssVmBkQ\nhVtsFQ7MPDkkw6BGhUMkRzKIZCRVOStNisWrM3IbSIzmTQteUx8+ViHuzaBw\n5ZCHgpwmVU0nZaWXN83FGXJEwaovRK/4L8Rj9i53Kklr3u5zx1/bBRioiDFU\nzgviFNCjsYeBDk88/7SrsBFAEV8dEX27vCpZ6k4ZBPx23UfXRyd7+3IGzyWn\nWEuDVtskDkzXBF9DABq16EdwU8uHJ5ycGUHySEKAEwdKTGAdufwIYPCcJdpR\ncGjk3xKpa/5B5kctnP42+ipIFVCsu+UGw0DGmhIHH950W8D4eKTLkBPdW7M4\nZEUKCLPMRAq+JBX2snMUqVA53FKRm4Nuun5AK9r20MhiuGHhj4T9YNiGLWhl\nGYS+ENvxEnf6Kq+X9sVNzKJ85ML6YpjdgFdiQSLPSNV9FrZznQT2UKVuy/4a\nlng1c5xFghWYRWqF+2XR08Ko+Z0oVups25LW+iyUt9ReS9rTmx92v/G1WbNN\ng8HOGhlaRluimUjWhJaNsP/pSmyeOuY8GdxsyfdA2e6sZsFFRUUl2zbLAI6M\n6nMY\r\n=Uoqk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDvo94FoAsy79KUQlBI953EowpnelXIzGcTaBPw5f9EgAIhAJvdEGHNnXWqJsu9cJNMdhGbcQd6wYtxQDBucaQ4Vsf4"}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.26_1562940138950_0.4779544240626561"},"_hasShrinkwrap":false},"0.0.28-beta.20190712.4":{"name":"@prftesp/cle-logger","version":"0.0.28-beta.20190712.4","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿CLE Logger\n===\n\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\n\n## Documentation\n\n- [Target Environment](#target-environment)\n- [Simple Example](#simple-example)\n- [Installation](#installation)\n- [Features](#features)\n- [Configuration](#configuration)\n- [API](#api)\n\t- [Logging levels](#logging-levels)\n\t- [Logging parameters](#logging-parameters)\n\t- [Available component names](#available-component-names)\n\t- [Available custom property keys](#available-custom-property-keys)\n\t- [Log codes](#log-codes)\n\t- [Registering Twilio Actions](#registering-twilio-actions)\n\t- [Version](#version)\n- [Log Object](#log-object)\n- [Advanced Configuration](#advanced-configuration)\n\t- [Log sinks](#log-sinks)\n\t- [Remote logging](#remote-logging)\n- [Changelog](#changelog)\n\n## Target Environment\n\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\n\n## Simple Example\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n// will log message to console\ncleLogger.info(\"log message\");\n```\n\n## Installation\n\n```bash\n$ npm install @prftesp/cle-logger\n```\n\n## Features\n\n- Runs in Node.js and the browser\n- Remote logging\n- Multiple custom log sinks\n- Remote minimum log level overrides\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\n- Support for tagging logs with custom component names or correlation tokens\n\n## Configuration\n\nThe logger can be used without configuration, and will by default log to console with a minimum logging level set to ```VERBOSE```.\n\nTo configure the logger call the ```init``` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\n\n**Available options**\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// Twilio account sid, required for use with CLE\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\n\n\t// Will be associated with all logs,\n\t// if not provided, a unique uuid will be generated\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\n\n\t// An array of custom log sinks that will receive an array of log objects,\n\t// and can optionally return a promise\n\tcustomLogSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: logs => console.log(logs),\n\t}],\n\n\t// A way to suppress error messages if set to false, defaults to true\n\tlogErrorsToConsole: true,\n\n\t// To support remote logging, add a url to receive logs\n\tlogUrl: \"https://example.com/api/logs\",\n\n\t// Will not send logs to the default remote log sink configured with 'logUrl'\n\tdisableRemoteLogging: false,\n\n\t// If logging Twilio Flex Actions, provide a list of actions \n\t// to be excluded from logs\n\texcludedFlexActionNames: [\"SendTyping\"],\n\n\t// Add console as a log sink, useful for debugging, defaults to true\n\tlogToConsole: true,\n\n\t// Provide a min log level, defaults to INFO\n\t// Use cleLogger.logLevels for predefined constants\n\tminLogLevel: cleLogger.logLevels.INFO,\n\n\t// A custom endpoint to fetch log level overrides\n\t// NOTE: the provided url should always end with 'accountSid=',\n\t// the library will append the accountSid\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\n\n\t// An instance of ('@twilio/flex-ui').Manager\n\ttwilioFlexManager: manager,\n});\n```\n\n## API\n\nThe logger can be imported as an object in CommonJs or ES6 syntax:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n// or\nconst cleLogger = require(\"@prftesp/cle-logger\");\n```\n\nAlternatively, all top level members of the ```cleLogger``` are available as named exports and can be imported separately:\n\n```js\nimport { info, componentNames } from \"@prftesp/cle-logger\";\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\n\n// vs\n\nimport { cleLogger } from \"@prftesp/cle-logger\";\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\n```\n\n### Logging levels\n\nLogging level constants can be found in ```cleLogger.logLevels```.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.logLevels.VERBOSE; // verbose\ncleLogger.logLevels.INFO; // info\ncleLogger.logLevels.WARN; // warn\ncleLogger.logLevels.ERROR; // error\ncleLogger.logLevels.CRITICAL; // critical\n```\n\nThere are functions available for each supported logging level:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(...);\ncleLogger.info(...);\ncleLogger.warn(...);\ncleLogger.error(...);\ncleLogger.critical(...);\n```\n\nThe functions are all async, return ```Promise<void>```, and can be optionally awaited. The suggested approach is to not await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is to suggested to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\n\n### Logging parameters\n\nAll logging functions take the same parameters:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(\n\t// The only required property, no logs will be generated without it\n\t\"Log message\",\n\n\t// Any additional log data you would like to include with the log,\n\t// can be any data type\n\t{ data: \"test\" },\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\t// CLE uses it to index these properties in Application Insights\n\t// For CLE, try to use keys found in 'cleLogger.customPropertyKeys'\n\t{ userId: 123 },\n\n\t// Another useful way to 'tag' your logs for easier searching\n\t// Can be any string, but for CLE, try to use available options in 'cleLogger.componentNames'\n\t// If you would like more component names added, please let us know \n\t\"ProfileComponent\",\n\n\t// Log code, can be any number used to categorize logs,\n\t// there will be a separate document listing CLE codes\n\t70123,\n\n\t// Correlation token to be associated with this log only,\n\t// if one is not provided, the token supplied/generated during 'init' will be used\n\t\"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\"\n);\n```\n\n### Available component names\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.componentNames.SESSION;\ncleLogger.componentNames.DIALER;\ncleLogger.componentNames.DASHBOARD;\ncleLogger.componentNames.AFTERCALL;\ncleLogger.componentNames.SUPERVISOR;\n```\n\n### Available custom property keys\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.customPropertyKeys.CHANNEL_SID;\ncleLogger.customPropertyKeys.TASK_SID;\ncleLogger.customPropertyKeys.CALL_SID;\ncleLogger.customPropertyKeys.WORKFLOW_SID;\ncleLogger.customPropertyKeys.WORKER_SID;\n```\n\n### Log codes\n\n[TODO]\n\n### Registering Twilio Actions\n\nExample in a Twilio Flex UI plugin:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n/**\n * @param flex { typeof import('@twilio/flex-ui') }\n * @param manager { import('@twilio/flex-ui').Manager }\n */\ninit(flex, manager) {\n\tcleLogger.init({\n\t\taccountSid: \"your-account-sid\"\n\t});\n\n\tcleLogger.registerTwilioActions(flex);\n}\n```\n\n**Note**: A Twilio account sid must be provided during ```init``` with the ```accountSid``` parameter, otherwise the actions won't be logged.\n\nThe logger will loop through all actions found under ```flex.Actions.actions```, and attach ```after``` event handlers to log the action name and payload. Any private properties found in the payload (starting with '_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Any actions names configured in ```excludedFlexActionNames``` will not be included in the logs.\n\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\n\n**Twilio Actions log levels**\n\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of ```INFO```.\n\n``` js\nAcceptTask: INFO\nCancelTransfer: INFO\nCompleteTask: INFO\nHangupCall: INFO\nHideDirectory: VERBOSE\nHistoryGo: VERBOSE\nHistoryGoBack: VERBOSE\nHistoryGoForward: VERBOSE\nHistoryPush: VERBOSE\nHistoryReplace: VERBOSE\nHoldCall: INFO\nHoldParticipant: INFO\nKickParticipant: INFO\nLogout: INFO\nMonitorCall: INFO\nNavigateToView: INFO\nRejectTask: INFO\nSelectTask: INFO\nSelectTaskInSupervisor: INFO\nSelectWorkerInSupervisor: INFO\nSendMessage: INFO\nSendTyping: VERBOSE\nSetActivity: INFO\nSetInputText: VERBOSE\nShowDirectory: VERBOSE\nStopMonitoringCall: INFO\nToggleMute: INFO\nToggleSidebar: VERBOSE\nTransferTask: INFO\nUnholdCall: INFO\nUnholdParticipant: INFO\nWrapupTask: INFO\n```\n\n### Version\n\nVersion will be automatically included in all logs.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.version; // 1.0.0\n```\n\n## Log Object\n\n```js\n{\n\t// Account sid if provided during init\n\taccountSid: string;\n\n\t// Any additional log data to be included with the log\n\tadditionalData: unknown;\n\t\n\t// Component name, if provided\n\tcomponentName: string;\n\n\t// Generated or configured token\n\tcorrelationToken: string;\n\n\t// Current UTC datetime in ISO 8601 format\n\teventDate: string;\n\n\t// Log level\n\tlevel: string;\n\n\t// Log code, if provided\n\tlogCode: number;\n\n\t// Log message\n\tmessage: string;\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\tproperties: object;\n\n\t// BrowserFunction if the environment is the browser\n\t// CloudFunction if the environment is Node.js\n\tsource: string;\n\n\t// Logging library version \n\tloggerVersion: string;\n}\n```\n\n## Advanced Configuration\n\n### Log sinks\n\nCustom log sinks can be provided via the ```customLogSinks``` config. It's an array of objects containing a ```name``` and a ```log``` method. All provided log sinks, along with the built-in remote and console log sinks will be executed in parallel. You can choose to await the logs to make sure all logging operations are completed.\nThe ```log``` method will receive an array of log objects for you to handle. If you're doing async requests in your custom log sinks, it is recommended to return the Promise.\n\nExample logging to file in Node.js:\n\n``` js\nconst fs = require(\"fs\");\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\ncleLogger.init({\n\tcustomLogSinks: [{\n\t\tname: \"testFileSink\",\n\t\tlog: logs => {\n\t\t\tfs.appendFile(\n\t\t\t\t\"./log.txt\",\n\t\t\t\t`${JSON.stringify(logs)}`,\n\t\t\t\tconsole.log\n\t\t\t);\n\t\t}\n\t}]\n});\n```\n\n### Remote logging\n\nThe library provides a way to provide you own remote log url, or to remotely override the min log level. This can be useful for temporarily setting the log level to verbose in a production environment, for quick troubleshooting.\n\n**Remote log endpoint**\n\nTo implement your own remote logging endpoint, all you have to do is accept ```POST``` requests, with a JSON payload of an array of [log objects](#log-object), and return either ```{ success: true }``` or ```{ success: false }```. The endpoint should always return a ```200 OK``` status code. A good use-case would be leveraging a serverless api platform to output logs to a queue.\n\nExample Azure Function:\n\n```js\nmodule.exports = async function (context, req) {\n    context.log(req.body); // body will have the log array\n\n    context.res = {\n        status: 200,\n        body: { success: true }\n    };\n};\n```\n\n**Remote log level override**\n\nThe ```minLogLevelUrl``` configuration property is used to provide an endpoint that accepts ```GET``` requests and a ```accountSid``` query string parameter. ```accountSid``` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the ```minLogLevel``` configuration property. If null is returned, the ```minLogLevel``` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a ```200 OK``` status code and use the ```success``` property to indicate failure.\n\nExample success response:\n\n```json\n{ \n\t\"success\": true,\n\t\"minLogLevel\": \"warn\"\n}\n```\n\nExample error response:\n\n```json\n{ \n\t\"success\": false,\n\t\"minLogLevel\": null\n}\n```\n\n## Changelog\n\n### v1.0.0 [2019-11-07]\n\n- Initial release","readmeFilename":"README.md","_id":"@prftesp/cle-logger@0.0.28-beta.20190712.4","_nodeVersion":"10.16.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-G6x5qrwmZco4QkzhIYCaZTkZjgFuROMrtPUNB8Xk0x+FKDQwFAL+9Bhn1VNFdYFgv2yKmH7X9MAin3+fuK88Bg==","shasum":"a328eae6ae3985dcfd9c90132bbac48cb50043c6","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.28-beta.20190712.4.tgz","fileCount":17,"unpackedSize":46394,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdKLAFCRA9TVsSAnZWagAA0KYP/2b6zyH5FZPACLU0Gkbo\nZ//KekQEzIKiELFV3QRlY6P+xXPIf/IboSaEkXhTNprRB0b5ce/1Zj3ES5S+\nRgBJRYACE632I/H/h85BApJ6n6S1N5xoUpE4cuzVZ1qlHFOF+CFwus+ozVWH\nRzkhD4nNMXttbGdpNdUWq+V52LKUaKI0Th6HpV93665FcYYPS3JsnZK7S1lN\nYqmZ25CA7dw+egaH7wtVw2+wj/+bq1dw2fPERCWaQYd5ik3LehZn9/nenAvD\nqoLc1wpO+Ezor+QW3LcBvCtXPYmj0mCmMULLBoqXcrZmmTlWbkY8JhIOjlya\nG9T36oFFynzyI/KVAFctg8AnQ82m32g0Vqh06Ejj099CVQGW84xL63gB6/fb\nknbW+dnWU3K2QYceLuq2bIPTgyYpQ5zjp1hCry5DqtZdDSu4/Tgg9JqSh+JY\nYVIhPgsrKnAU0PK9nwHp7Us1DcGwDtAPRgOfNrPL6vPcYEdJ3j3WKOj/bhIf\nxbIPa5F8DY6ssSbCWQB244f1G+26zS8VlRnfcXxQL56oDoTORi3Rl621dJsX\nzIErlUXnRJUM0GfqfwrCRTYEHS6eHbzp/7XCED6y0uVGme6/dpTexF+2qvuu\nFOPhuKKC4GlGJNmncic8SQspAra85ylKiOjNBV64mH5mvZI7+Uzz88T2byTq\n8z3u\r\n=Yo4+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAQSz6aQX3ad+ulvMcDLcts5Bpk0VHE8vH3wU11XqxcNAiEA2+8GQo66OPynKAkKCwcxPWsC4BDYiO30+9Xo2Saiies="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.28-beta.20190712.4_1562947588844_0.1661002088627972"},"_hasShrinkwrap":false},"0.0.28":{"name":"@prftesp/cle-logger","version":"0.0.28","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@0.0.28","_nodeVersion":"10.16.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-sV2q7txTG2wSeCeI09iada2n0czPWV1YpWJj7rlekKj1KiHwrlX7em09yCYJGcrcmmBhPk/j/jD8TQqmzK5ZxQ==","shasum":"228f71406955aa8d0674173cfad34f296dd8cbd2","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.28.tgz","fileCount":17,"unpackedSize":46295,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdKLC8CRA9TVsSAnZWagAA6DMP/2rB6XPZYFQ8f4CXvQLU\nDyupsDz1CkWMg+iHAKarK50sSKqZe8tdS9Zj7435wJCfdyAFcuJ5RNq0PHgv\nAKfFiaFtScNzz5xkLYTxoWOdqMEpXzyeFWixJNue37G0+N4Gvi3wWrbUVCkc\negZ2YKr5uFMei7Iwp2Ndzt5ZJelwfJtDM3upFrORns/gIwfN8KHJnFKsapl0\n8L6K77h7Zk78LF078223flGL4F4vuMEUNQSwR4m/9vluo8dFS72R39UrNqL2\ntq5dMV4+DFkzGYIPPQitKMo7/uH5VAlS4fgYSXKjUPnhzO4vtOssj5U45ol8\n7dLkI+Cd0jmRlZo+fOleLzFMJ0T4lm5RsxMBEuvDFEpttTTFkTBX8PO/fqRz\nUW0Qe3Sxv8grb6Wb+nWMu8AYr5Xx+TCvFMFSqZgTRxjmvOG8tPSjcheqy4Pu\nh590jC8WKQCX5KU+SoiFAI9U6zld7kf7JdFlxX9fCwXhAXel4Bne0t2r8E8H\n+j4ad2EOVEe98N9R3pZZ5+3DGhYPzqgAh9D8P+cy2ia6AjGDtNzLLPR5Yl9b\n0PCrULS8/oQORnSWjwdU6CBQjD++t/U3y//9mKVfrg9f6AF+wKlW9a/DhrOx\nRjXI5s0RCh+vc9DOWpGRn5Xi5yijwKHNJBwEpP26XDbPfwV7o7M3XuLL5iz6\nwNL6\r\n=vH3O\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIALO38VbMoRRwBwUADNqXy/wYaEXOvZUcDahcdJs3UNGAiAgiqzG8qx7zfiY0AhwBNrKIoBcSJP+qlAUYxI3G1eGog=="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.28_1562947772136_0.9236114295927582"},"_hasShrinkwrap":false},"0.0.29":{"name":"@prftesp/cle-logger","version":"0.0.29","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@0.0.29","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-My2EmUmFAS01V/QeyIeyf071wW4ZzXJIDGwxGa2r0M6kxNgLzqAiqa89/en2VNPxXWi6XG9/Jgu+YD6WBwcyGQ==","shasum":"91165a2f2a0d318b2102e4de781df7f2c4bb7414","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.29.tgz","fileCount":17,"unpackedSize":47384,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdLfpUCRA9TVsSAnZWagAAZOkP/3vse05VnvGncUIads0+\nlT1fiMuzsNJ6okJ5iNPBe5mgzbh7/FyhkM23P+ZS/PySHRB4WtVkGXxPkBQ+\nSjsU1d04d7z8skolRNdKBOfacDXANjNMWPK+OUIOPVFwRXR8u72BdCo/cSjM\nk2sgr/ERN15HFvuc2zm51igAxWX2f2SQpXM2jj1KJUg+pNJ0jRAxAOWAx6zS\nFDcv4aKtxifyY6SnI+aikmSWQpyLKsAabwOA5YXGcKYtU6WFUirgPwBciXM4\nGOOpc/XskIdi90RQ28UqFi44MMLNXEVte14ozTeh46BEcs7Y5tvdh8H/BI7q\nTV4MVAcnPKe9Lgee8s1zAayiktFal0Rmab/qMn8uDIqgXQ4JCDAjcR11wzi9\nLkdR2a8LouJFUxWAl1REMTlq47dHucQH3DaoXy9L0FNUoXY5NRG5SV52zawj\ncvCJVHMzN945FVeuKgZ7X2gKazCCjhXKX+ESD2Qfy7lBX7lptoOejE9VX9lK\nDdwf3wAGbuOsQ9VmgMSPu2EVXzVtjvdEVBSFzyxtn5ZN31+FOp+OAIOsEBXW\n2aeQoEknZCArq1DxbceksE5zUeHpd1teTuYtFfSSimkwrg7q8RpN8EwDMsDH\nXZYt8mB5DWS3qvSzCH4QH42vs/jrAXukp9SnjI03OGIHs0yIglkQFkUQdAHb\nxv9q\r\n=qkOj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDhfcV9MEJht9YcP25OBwgpIWzGCOikojzm14v5AUxovgIhALMkJqWjwi7EplIKadroPAYn2DDCdXDROHx45Jsm5aSJ"}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.29_1563294291174_0.0767989307755721"},"_hasShrinkwrap":false},"0.0.30-beta.20190731.1":{"name":"@prftesp/cle-logger","version":"0.0.30-beta.20190731.1","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿CLE Logger\n===\n\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\n\n## Documentation\n\n- [Target Environment](#target-environment)\n- [Simple Example](#simple-example)\n- [Installation](#installation)\n- [Features](#features)\n- [Configuration](#configuration)\n- [API](#api)\n\t- [Logging levels](#logging-levels)\n\t- [Logging parameters](#logging-parameters)\n\t- [Available component names](#available-component-names)\n\t- [Available custom property keys](#available-custom-property-keys)\n\t- [Log codes](#log-codes)\n\t- [Registering Twilio Actions](#registering-twilio-actions)\n\t- [Version](#version)\n- [Log Object](#log-object)\n- [Advanced Configuration](#advanced-configuration)\n\t- [Log sinks](#log-sinks)\n\t- [Remote logging](#remote-logging)\n- [Changelog](#changelog)\n\n## Target Environment\n\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\n\n## Simple Example\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n// will log message to console\ncleLogger.info(\"log message\");\n```\n\n## Installation\n\n```bash\n$ npm install @prftesp/cle-logger\n```\n\n## Features\n\n- Runs in Node.js and the browser\n- Remote logging\n- Multiple custom log sinks\n- Remote minimum log level overrides\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\n- Support for tagging logs with custom component names or correlation tokens\n\n## Configuration\n\nThe logger can be used without configuration, and will by default log to console with a minimum logging level set to ```VERBOSE```.\n\nTo configure the logger call the ```init``` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\n\n**Available options**\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// Twilio account sid, required for use with CLE\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\n\n\t// Will be associated with all logs,\n\t// if not provided, a unique uuid will be generated\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\n\n\t// An array of custom log sinks that will receive an array of log objects,\n\t// and can optionally return a promise\n\tcustomLogSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: logs => console.log(logs),\n\t}],\n\n\t// A way to suppress error messages if set to false, defaults to true\n\tlogErrorsToConsole: true,\n\n\t// To support remote logging, add a url to receive logs\n\tlogUrl: \"https://example.com/api/logs\",\n\n\t// Will not send logs to the default remote log sink configured with 'logUrl'\n\tdisableRemoteLogging: false,\n\n\t// If logging Twilio Flex Actions, provide a list of actions \n\t// to be excluded from logs\n\texcludedFlexActionNames: [\"SendTyping\"],\n\n\t// Add console as a log sink, useful for debugging, defaults to true\n\tlogToConsole: true,\n\n\t// Provide a min log level, defaults to INFO\n\t// Use cleLogger.logLevels for predefined constants\n\tminLogLevel: cleLogger.logLevels.INFO,\n\n\t// A custom endpoint to fetch log level overrides\n\t// NOTE: the provided url should always end with 'accountSid=',\n\t// the library will append the accountSid\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\n\n\t// An instance of ('@twilio/flex-ui').Manager\n\ttwilioFlexManager: manager,\n});\n```\n\n## API\n\nThe logger can be imported as an object in CommonJs or ES6 syntax:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n// or\nconst cleLogger = require(\"@prftesp/cle-logger\");\n```\n\nAlternatively, all top level members of the ```cleLogger``` are available as named exports and can be imported separately:\n\n```js\nimport { info, componentNames } from \"@prftesp/cle-logger\";\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\n\n// vs\n\nimport { cleLogger } from \"@prftesp/cle-logger\";\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\n```\n\n### Logging levels\n\nLogging level constants can be found in ```cleLogger.logLevels```.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.logLevels.VERBOSE; // verbose\ncleLogger.logLevels.INFO; // info\ncleLogger.logLevels.WARN; // warn\ncleLogger.logLevels.ERROR; // error\ncleLogger.logLevels.CRITICAL; // critical\n```\n\nThere are functions available for each supported logging level:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(...);\ncleLogger.info(...);\ncleLogger.warn(...);\ncleLogger.error(...);\ncleLogger.critical(...);\n```\n\nThe functions are all async, return ```Promise<void>```, and can be optionally awaited. The suggested approach is to not await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is to suggested to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\n\n### Logging parameters\n\nAll logging functions take the same parameters:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(\n\t// The only required property, no logs will be generated without it\n\t\"Log message\",\n\n\t// Any additional log data you would like to include with the log,\n\t// can be any data type\n\t{ data: \"test\" },\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\t// CLE uses it to index these properties in Application Insights\n\t// For CLE, try to use keys found in 'cleLogger.customPropertyKeys'\n\t{ userId: 123 },\n\n\t// Another useful way to 'tag' your logs for easier searching\n\t// Can be any string, but for CLE, try to use available options in 'cleLogger.componentNames'\n\t// If you would like more component names added, please let us know \n\t\"ProfileComponent\",\n\n\t// Log code, can be any number used to categorize logs,\n\t// there will be a separate document listing CLE codes\n\t70123,\n\n\t// Correlation token to be associated with this log only,\n\t// if one is not provided, the token supplied/generated during 'init' will be used\n\t\"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\"\n);\n```\n\n### Available component names\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.componentNames.SESSION;\ncleLogger.componentNames.DIALER;\ncleLogger.componentNames.DASHBOARD;\ncleLogger.componentNames.AFTERCALL;\ncleLogger.componentNames.SUPERVISOR;\n```\n\n### Available custom property keys\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.customPropertyKeys.CHANNEL_SID;\ncleLogger.customPropertyKeys.TASK_SID;\ncleLogger.customPropertyKeys.CALL_SID;\ncleLogger.customPropertyKeys.WORKFLOW_SID;\ncleLogger.customPropertyKeys.WORKER_SID;\n```\n\n### Log codes\n\n[TODO]\n\n### Registering Twilio Actions\n\nExample in a Twilio Flex UI plugin:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n/**\n * @param flex { typeof import('@twilio/flex-ui') }\n * @param manager { import('@twilio/flex-ui').Manager }\n */\ninit(flex, manager) {\n\tcleLogger.init({\n\t\taccountSid: \"your-account-sid\"\n\t});\n\n\tcleLogger.registerTwilioActions(flex);\n}\n```\n\n**Note**: A Twilio account sid must be provided during ```init``` with the ```accountSid``` parameter, otherwise the actions won't be logged.\n\nThe logger will loop through all actions found under ```flex.Actions.actions```, and attach ```after``` event handlers to log the action name and payload. Any private properties found in the payload (starting with '_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Any actions names configured in ```excludedFlexActionNames``` will not be included in the logs.\n\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\n\n**Twilio Actions log levels**\n\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of ```INFO```.\n\n``` js\nAcceptTask: INFO\nCancelTransfer: INFO\nCompleteTask: INFO\nHangupCall: INFO\nHideDirectory: VERBOSE\nHistoryGo: VERBOSE\nHistoryGoBack: VERBOSE\nHistoryGoForward: VERBOSE\nHistoryPush: VERBOSE\nHistoryReplace: VERBOSE\nHoldCall: INFO\nHoldParticipant: INFO\nKickParticipant: INFO\nLogout: INFO\nMonitorCall: INFO\nNavigateToView: INFO\nRejectTask: INFO\nSelectTask: INFO\nSelectTaskInSupervisor: INFO\nSelectWorkerInSupervisor: INFO\nSendMessage: INFO\nSendTyping: VERBOSE\nSetActivity: INFO\nSetInputText: VERBOSE\nShowDirectory: VERBOSE\nStopMonitoringCall: INFO\nToggleMute: INFO\nToggleSidebar: VERBOSE\nTransferTask: INFO\nUnholdCall: INFO\nUnholdParticipant: INFO\nWrapupTask: INFO\n```\n\n### Version\n\nVersion will be automatically included in all logs.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.version; // 1.0.0\n```\n\n## Log Object\n\n```js\n{\n\t// Account sid if provided during init\n\taccountSid: string;\n\n\t// Any additional log data to be included with the log\n\tadditionalData: unknown;\n\t\n\t// Component name, if provided\n\tcomponentName: string;\n\n\t// Generated or configured token\n\tcorrelationToken: string;\n\n\t// Current UTC datetime in ISO 8601 format\n\teventDate: string;\n\n\t// Log level\n\tlevel: string;\n\n\t// Log code, if provided\n\tlogCode: number;\n\n\t// Log message\n\tmessage: string;\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\tproperties: object;\n\n\t// BrowserFunction if the environment is the browser\n\t// CloudFunction if the environment is Node.js\n\tsource: string;\n\n\t// Logging library version \n\tloggerVersion: string;\n}\n```\n\n## Advanced Configuration\n\n### Log sinks\n\nCustom log sinks can be provided via the ```customLogSinks``` config. It's an array of objects containing a ```name``` and a ```log``` method. All provided log sinks, along with the built-in remote and console log sinks will be executed in parallel. You can choose to await the logs to make sure all logging operations are completed.\nThe ```log``` method will receive an array of log objects for you to handle. If you're doing async requests in your custom log sinks, it is recommended to return the Promise.\n\nExample logging to file in Node.js:\n\n``` js\nconst fs = require(\"fs\");\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\ncleLogger.init({\n\tcustomLogSinks: [{\n\t\tname: \"testFileSink\",\n\t\tlog: logs => {\n\t\t\tfs.appendFile(\n\t\t\t\t\"./log.txt\",\n\t\t\t\t`${JSON.stringify(logs)}`,\n\t\t\t\tconsole.log\n\t\t\t);\n\t\t}\n\t}]\n});\n```\n\n### Remote logging\n\nThe library provides a way to provide you own remote log url, or to remotely override the min log level. This can be useful for temporarily setting the log level to verbose in a production environment, for quick troubleshooting.\n\n**Remote log endpoint**\n\nTo implement your own remote logging endpoint, all you have to do is accept ```POST``` requests, with a JSON payload of an array of [log objects](#log-object), and return either ```{ success: true }``` or ```{ success: false }```. The endpoint should always return a ```200 OK``` status code. A good use-case would be leveraging a serverless api platform to output logs to a queue.\n\nExample Azure Function:\n\n```js\nmodule.exports = async function (context, req) {\n    context.log(req.body); // body will have the log array\n\n    context.res = {\n        status: 200,\n        body: { success: true }\n    };\n};\n```\n\n**Remote log level override**\n\nThe ```minLogLevelUrl``` configuration property is used to provide an endpoint that accepts ```GET``` requests and a ```accountSid``` query string parameter. ```accountSid``` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the ```minLogLevel``` configuration property. If null is returned, the ```minLogLevel``` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a ```200 OK``` status code and use the ```success``` property to indicate failure.\n\nExample success response:\n\n```json\n{ \n\t\"success\": true,\n\t\"minLogLevel\": \"warn\"\n}\n```\n\nExample error response:\n\n```json\n{ \n\t\"success\": false,\n\t\"minLogLevel\": null\n}\n```\n\n## Changelog\n\n### v1.0.0 [2019-11-07]\n\n- Initial release","readmeFilename":"README.md","_id":"@prftesp/cle-logger@0.0.30-beta.20190731.1","_nodeVersion":"10.16.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-FBZiuhW02NNLnmTtL5AOBoEOYecCrp3hp8fakhf3egDWFi0os3Z8GsGIW0DzWIIaCkLrg4Cmk+0n4h1J4eAVDw==","shasum":"d470a38c831cdd47820e2dbb39acf85c85d0140b","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.30-beta.20190731.1.tgz","fileCount":17,"unpackedSize":46848,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdQbr8CRA9TVsSAnZWagAALbcP/Rv3hUZdFRh+5ha54b1C\ncR6s6mHb3Ytp/2hVIymTqn48zBqV+rjAvhGlaUv+L7D2JEYWplcZOrX25lWQ\n4oa4MXA3kWqBOFsozJri9lJDOzPMrE11odyFey2YYERtOwkSnI6Z2aV3Gogn\n874zwwl3Nqs8/PyJT7bJOD7Als5G2MYEYqwie+G1LJYuGQy09mNcN8bpipce\nn00us4xj8pqYit97RYgR57Ibkr02Cwhqzq0gTQIk3fFRoKHclag2PscX2OkZ\n1blLuDWzT4tyMDWw7Eq9vDpp2z3CCYJ5nd+99WcD41DcVb7SoDkaNok4QRfq\ncFgSvvPhIjA95w8KkT8KdtPvD0EC7fVrKEi+/kkjMWb9n+ju71mPJdjLrYDh\n8fdYgcTA8PUOifQ0HaalL4uW/kRHgVer2y8fikozCXu+tp4OGAPHgVe8vxpV\nvNQqmVwriu9XjdTlwPZpxJz2Iru4euqfqpvo4vAotnqGS0nbJrdKZxeigzsR\n08um1fQFUorD1gs4pj13oVgBXMc86vbpEMwxWu7oto3nztbPgvbKJiBc2OJb\nYxa0dDbdKFwXjMXTCM/dFLyqBbd1V+0RnEtBnJ1/Oh7WSdpQQiUD762uSn03\nKL0XTswyaPEEsf8H+agFTRZZQYB8nQu1GK6EVK/XaUtKNkp6I74P8HBHl+2n\ntHjG\r\n=VS2b\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH6ge+5YdJDDX1BXE353xh5X+GRTLhSF/w2Vlgco239AAiEAzWUhI7MZyDHsEAb6J88MSJgLBwalPGZo1J9XL5KbL6Q="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.30-beta.20190731.1_1564588795827_0.699666458322691"},"_hasShrinkwrap":false},"0.0.30":{"name":"@prftesp/cle-logger","version":"0.0.30","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@0.0.30","_nodeVersion":"10.16.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-bYCtihm99OyY8EjGSRm3iZsonHAUJ3GOVGepVxA2VvYkcXs1iLF/R0OnPa+yOQ3SHLbfthTsGCi/Wrbe+qKlbA==","shasum":"8e3cfd7538133813d2ca3457289f0f7396deacbc","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.30.tgz","fileCount":17,"unpackedSize":46749,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdQbuyCRA9TVsSAnZWagAAEFcP/1vr0sPcX8+MM4FIXCpz\nL02pHziJXYIlZ3TBITHgyusxOX7+S5ybr0DJ0n0Jf97bUG/4E5gqdbrWZYo9\nTJKHFA8A5kRUvKcTzMEOn9qkWYyXvXw9MQJ+OqKnL+RoQOFAoGSOZ7cr9ZyJ\nwb7GdBlTFilsq1MNy38nlPCmeFP31wtN3//spW60Ckw6Jh/+sGDeuwTc0J6X\nCWaF/YdBsYEdu3/7bcF89s41Ec8LTTD9wOPeeE8LM7ie8Zs6Kp8+Xl2EhwPJ\nf9DKm2HN2QDmCS3gHv1/0pMyXQap/kvQ8mAFiXZAxe+6hMpTcwu0n16z5Wcm\nB31zm1XOh0kv9OZ+yQC7esm5e95zU9rz66c+5XS6MdOKmaqN6xt8dIomcPPG\nvTq8dlvRhRy28ugIin2577UqJfVCokK5sNQ7ecup+t/cQbTjefjhWghPzYzg\niX9UVwjXf26Uf07sJE43369abSi9gFc+YAht+aUYZWEE4G3xtGak5fMoe3Ui\ncmcHi0WBeszp0R1vYysC88384XfU8UzgDD4e0tILdfefJtCHpL01jgmRv/Yj\nuzdQV7D9S4P3tbMu0Bedxk0nhryTLntgPHEWNraHLGoZjKWUKJg5VhTOnQJr\n9MIMBiFnWeGIsy0LwTcQRUCJjctZ6N/Oa5uC94PHjOvxJKHPpyFZG85nYeyq\n8HNt\r\n=IduU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGH2ZmFUj7LL7GqqLU9q0nPd4YnGXwmRWfjjyYyPvtBPAiAL/AUkHgOhVLGB0tXR4FLOL2lmajkeWxnJpI98T8xfEw=="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.30_1564588978189_0.33600458268732303"},"_hasShrinkwrap":false},"0.0.30-beta.20190830.2":{"name":"@prftesp/cle-logger","version":"0.0.30-beta.20190830.2","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿CLE Logger\n===\n\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\n\n## Documentation\n\n- [Target Environment](#target-environment)\n- [Simple Example](#simple-example)\n- [Installation](#installation)\n- [Features](#features)\n- [Configuration](#configuration)\n- [API](#api)\n\t- [Logging levels](#logging-levels)\n\t- [Logging parameters](#logging-parameters)\n\t- [Available component names](#available-component-names)\n\t- [Available custom property keys](#available-custom-property-keys)\n\t- [Log codes](#log-codes)\n\t- [Registering Twilio Actions](#registering-twilio-actions)\n\t- [Version](#version)\n- [Log Object](#log-object)\n- [Advanced Configuration](#advanced-configuration)\n\t- [Log sinks](#log-sinks)\n\t- [Remote logging](#remote-logging)\n- [Changelog](#changelog)\n\n## Target Environment\n\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\n\n## Simple Example\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n// will log message to console\ncleLogger.info(\"log message\");\n```\n\n## Installation\n\n```bash\n$ npm install @prftesp/cle-logger\n```\n\n## Features\n\n- Runs in Node.js and the browser\n- Remote logging\n- Multiple custom log sinks\n- Remote minimum log level overrides\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\n- Support for tagging logs with custom component names or correlation tokens\n\n## Configuration\n\nThe logger can be used without configuration, and will by default log to console with a minimum logging level set to ```VERBOSE```.\n\nTo configure the logger call the ```init``` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\n\n**Available options**\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// Twilio account sid, required for use with CLE\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\n\n\t// Will be associated with all logs,\n\t// if not provided, a unique uuid will be generated\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\n\n\t// An array of custom log sinks that will receive an array of log objects,\n\t// and can optionally return a promise\n\tcustomLogSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: logs => console.log(logs),\n\t}],\n\n\t// A way to suppress error messages if set to false, defaults to true\n\tlogErrorsToConsole: true,\n\n\t// To support remote logging, add a url to receive logs\n\tlogUrl: \"https://example.com/api/logs\",\n\n\t// Will not send logs to the default remote log sink configured with 'logUrl'\n\tdisableRemoteLogging: false,\n\n\t// If logging Twilio Flex Actions, provide a list of actions \n\t// to be excluded from logs\n\texcludedFlexActionNames: [\"SendTyping\"],\n\n\t// Add console as a log sink, useful for debugging, defaults to true\n\tlogToConsole: true,\n\n\t// Provide a min log level, defaults to INFO\n\t// Use cleLogger.logLevels for predefined constants\n\tminLogLevel: cleLogger.logLevels.INFO,\n\n\t// A custom endpoint to fetch log level overrides\n\t// NOTE: the provided url should always end with 'accountSid=',\n\t// the library will append the accountSid\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\n\n\t// An instance of ('@twilio/flex-ui').Manager\n\ttwilioFlexManager: manager,\n});\n```\n\n## API\n\nThe logger can be imported as an object in CommonJs or ES6 syntax:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n// or\nconst cleLogger = require(\"@prftesp/cle-logger\");\n```\n\nAlternatively, all top level members of the ```cleLogger``` are available as named exports and can be imported separately:\n\n```js\nimport { info, componentNames } from \"@prftesp/cle-logger\";\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\n\n// vs\n\nimport { cleLogger } from \"@prftesp/cle-logger\";\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\n```\n\n### Logging levels\n\nLogging level constants can be found in ```cleLogger.logLevels```.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.logLevels.VERBOSE; // verbose\ncleLogger.logLevels.INFO; // info\ncleLogger.logLevels.WARN; // warn\ncleLogger.logLevels.ERROR; // error\ncleLogger.logLevels.CRITICAL; // critical\n```\n\nThere are functions available for each supported logging level:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(...);\ncleLogger.info(...);\ncleLogger.warn(...);\ncleLogger.error(...);\ncleLogger.critical(...);\n```\n\nThe functions are all async, return ```Promise<void>```, and can be optionally awaited. The suggested approach is to not await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is to suggested to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\n\n### Logging parameters\n\nAll logging functions take the same parameters:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(\n\t// The only required property, no logs will be generated without it\n\t\"Log message\",\n\n\t// Any additional log data you would like to include with the log,\n\t// can be any data type\n\t{ data: \"test\" },\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\t// CLE uses it to index these properties in Application Insights\n\t// For CLE, try to use keys found in 'cleLogger.customPropertyKeys'\n\t{ userId: 123 },\n\n\t// Another useful way to 'tag' your logs for easier searching\n\t// Can be any string, but for CLE, try to use available options in 'cleLogger.componentNames'\n\t// If you would like more component names added, please let us know \n\t\"ProfileComponent\",\n\n\t// Log code, can be any number used to categorize logs,\n\t// there will be a separate document listing CLE codes\n\t70123,\n\n\t// Correlation token to be associated with this log only,\n\t// if one is not provided, the token supplied/generated during 'init' will be used\n\t\"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\"\n);\n```\n\n### Available component names\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.componentNames.SESSION;\ncleLogger.componentNames.DIALER;\ncleLogger.componentNames.DASHBOARD;\ncleLogger.componentNames.AFTERCALL;\ncleLogger.componentNames.SUPERVISOR;\n```\n\n### Available custom property keys\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.customPropertyKeys.CHANNEL_SID;\ncleLogger.customPropertyKeys.TASK_SID;\ncleLogger.customPropertyKeys.CALL_SID;\ncleLogger.customPropertyKeys.WORKFLOW_SID;\ncleLogger.customPropertyKeys.WORKER_SID;\n```\n\n### Log codes\n\n[TODO]\n\n### Registering Twilio Actions\n\nExample in a Twilio Flex UI plugin:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n/**\n * @param flex { typeof import('@twilio/flex-ui') }\n * @param manager { import('@twilio/flex-ui').Manager }\n */\ninit(flex, manager) {\n\tcleLogger.init({\n\t\taccountSid: \"your-account-sid\"\n\t});\n\n\tcleLogger.registerTwilioActions(flex);\n}\n```\n\n**Note**: A Twilio account sid must be provided during ```init``` with the ```accountSid``` parameter, otherwise the actions won't be logged.\n\nThe logger will loop through all actions found under ```flex.Actions.actions```, and attach ```after``` event handlers to log the action name and payload. Any private properties found in the payload (starting with '_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Any actions names configured in ```excludedFlexActionNames``` will not be included in the logs.\n\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\n\n**Twilio Actions log levels**\n\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of ```INFO```.\n\n``` js\nAcceptTask: INFO\nCancelTransfer: INFO\nCompleteTask: INFO\nHangupCall: INFO\nHideDirectory: VERBOSE\nHistoryGo: VERBOSE\nHistoryGoBack: VERBOSE\nHistoryGoForward: VERBOSE\nHistoryPush: VERBOSE\nHistoryReplace: VERBOSE\nHoldCall: INFO\nHoldParticipant: INFO\nKickParticipant: INFO\nLogout: INFO\nMonitorCall: INFO\nNavigateToView: INFO\nRejectTask: INFO\nSelectTask: INFO\nSelectTaskInSupervisor: INFO\nSelectWorkerInSupervisor: INFO\nSendMessage: INFO\nSendTyping: VERBOSE\nSetActivity: INFO\nSetInputText: VERBOSE\nShowDirectory: VERBOSE\nStopMonitoringCall: INFO\nToggleMute: INFO\nToggleSidebar: VERBOSE\nTransferTask: INFO\nUnholdCall: INFO\nUnholdParticipant: INFO\nWrapupTask: INFO\n```\n\n### Version\n\nVersion will be automatically included in all logs.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.version; // 1.0.0\n```\n\n## Log Object\n\n```js\n{\n\t// Account sid if provided during init\n\taccountSid: string;\n\n\t// Any additional log data to be included with the log\n\tadditionalData: unknown;\n\t\n\t// Component name, if provided\n\tcomponentName: string;\n\n\t// Generated or configured token\n\tcorrelationToken: string;\n\n\t// Current UTC datetime in ISO 8601 format\n\teventDate: string;\n\n\t// Log level\n\tlevel: string;\n\n\t// Log code, if provided\n\tlogCode: number;\n\n\t// Log message\n\tmessage: string;\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\tproperties: object;\n\n\t// BrowserFunction if the environment is the browser\n\t// CloudFunction if the environment is Node.js\n\tsource: string;\n\n\t// Logging library version \n\tloggerVersion: string;\n}\n```\n\n## Advanced Configuration\n\n### Log sinks\n\nCustom log sinks can be provided via the ```customLogSinks``` config. It's an array of objects containing a ```name``` and a ```log``` method. All provided log sinks, along with the built-in remote and console log sinks will be executed in parallel. You can choose to await the logs to make sure all logging operations are completed.\nThe ```log``` method will receive an array of log objects for you to handle. If you're doing async requests in your custom log sinks, it is recommended to return the Promise.\n\nExample logging to file in Node.js:\n\n``` js\nconst fs = require(\"fs\");\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\ncleLogger.init({\n\tcustomLogSinks: [{\n\t\tname: \"testFileSink\",\n\t\tlog: logs => {\n\t\t\tfs.appendFile(\n\t\t\t\t\"./log.txt\",\n\t\t\t\t`${JSON.stringify(logs)}`,\n\t\t\t\tconsole.log\n\t\t\t);\n\t\t}\n\t}]\n});\n```\n\n### Remote logging\n\nThe library provides a way to provide you own remote log url, or to remotely override the min log level. This can be useful for temporarily setting the log level to verbose in a production environment, for quick troubleshooting.\n\n**Remote log endpoint**\n\nTo implement your own remote logging endpoint, all you have to do is accept ```POST``` requests, with a JSON payload of an array of [log objects](#log-object), and return either ```{ success: true }``` or ```{ success: false }```. The endpoint should always return a ```200 OK``` status code. A good use-case would be leveraging a serverless api platform to output logs to a queue.\n\nExample Azure Function:\n\n```js\nmodule.exports = async function (context, req) {\n    context.log(req.body); // body will have the log array\n\n    context.res = {\n        status: 200,\n        body: { success: true }\n    };\n};\n```\n\n**Remote log level override**\n\nThe ```minLogLevelUrl``` configuration property is used to provide an endpoint that accepts ```GET``` requests and a ```accountSid``` query string parameter. ```accountSid``` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the ```minLogLevel``` configuration property. If null is returned, the ```minLogLevel``` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a ```200 OK``` status code and use the ```success``` property to indicate failure.\n\nExample success response:\n\n```json\n{ \n\t\"success\": true,\n\t\"minLogLevel\": \"warn\"\n}\n```\n\nExample error response:\n\n```json\n{ \n\t\"success\": false,\n\t\"minLogLevel\": null\n}\n```\n\n## Changelog\n\n### v1.0.0 [2019-11-07]\n\n- Initial release","readmeFilename":"README.md","_id":"@prftesp/cle-logger@0.0.30-beta.20190830.2","_nodeVersion":"10.16.3","_npmVersion":"6.10.3","dist":{"integrity":"sha512-3m6fQL5Wx7aU4muRB22Rr9EBtdEvpuGbhrQ+7lUwjSR599oUy6E24Zjb51BMdmYfDD8Pt5olWMzUEtOUSc3ffg==","shasum":"77480ec372cbe69ff1c393d49e044f4c2123a74d","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-0.0.30-beta.20190830.2.tgz","fileCount":17,"unpackedSize":47219,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdaUrjCRA9TVsSAnZWagAA334QAJEe9KWc7tWuYgiA0aOI\ndkUSMfuMOpNEazufPRyy+FNHb2kCRuD2FK7WozpE32wZvv+LqsGxGYaj6+Au\nnAEy9zgiURCRn3aG4+YPtxVboM5T01Ne01wDn5dH3TgEpN1zCcqiuqIqB762\nG6BpyL0UfO9yybqz2pF2N1IuKS+Cud9KtOdsQd1MC1xrFD/wj+LVX9CjBNN5\n7fzmsTLbZXHT3fD4iFXOu292wxY5CSrpVJndH4MGrEsVD7zPjNJnIKj2f9t1\nJniGBRrYEMgz8/aq4I82D6o3xEmewRe0753YvWr9UF1rDizv3OZiuPfTAwaZ\n17EVRHhT7qzt4dDMblPljnK4oHgfsNtd6GLdjRqrkQApyhGHHo+AEZ7lJ+pv\nJALzcSTP72ehkhWz6Y+A0tk+sMSEaDwuls0oy1M0Zh071GDMw4K/hCAY8780\n4VBR0TfDzj/V6lvHgl80URjgs6tjKNcOaZ6zmA0cmpbZr8ESryOwhVdTlS/5\ndEhff+dkxq0FB5uV/2Y72TOcWfXXvdHXAEHMzcrQCBmrNaL9MQ+0jJe70rAZ\nIgPqTkhO0xsVbBc3/lEjgTt9vkpH159AQMtwU2nv8trKoBOcreHakou826r0\nFQyBztflZdQDimv/Ib7vE6eENEdwzgLfqw3rGb+X+D5m7ol1uaRDLTC6Qxm7\n4p6X\r\n=77Ry\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCcjSeEprg3qFxk42PD8RJMjXkQT4FpxmdIeMFinlLoxgIhAK8iWfyPLzT+Z7q8vgQHlZf2jd0NQXAg2h8ENTeBcg8m"}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_0.0.30-beta.20190830.2_1567181538472_0.3685923025489326"},"_hasShrinkwrap":false},"1.0.0-beta.1":{"name":"@prftesp/cle-logger","version":"1.0.0-beta.1","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿CLE Logger\r\n===\r\n\r\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\r\n\r\n## Documentation\r\n\r\n- [Target Environment](#target-environment)\r\n- [Simple Example](#simple-example)\r\n- [Installation](#installation)\r\n- [Features](#features)\r\n- [Configuration](#configuration)\r\n- [API](#api)\r\n\t- [Logging levels](#logging-levels)\r\n\t- [Logging parameters](#logging-parameters)\r\n\t- [Available component names](#available-component-names)\r\n\t- [Available custom property keys](#available-custom-property-keys)\r\n\t- [Log codes](#log-codes)\r\n\t- [Registering Twilio Actions](#registering-twilio-actions)\r\n\t- [Twilio Event Hooks](#twilio-event-hooks)\r\n\t- [Version](#version)\r\n- [Log Object](#log-object)\r\n- [Advanced Configuration](#advanced-configuration)\r\n\t- [Log sinks](#log-sinks)\r\n\t- [Remote logging](#remote-logging)\r\n- [Changelog](#changelog)\r\n\r\n## Target Environment\r\n\r\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\r\n\r\n## Simple Example\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\n// will log message to console\r\ncleLogger.info(\"log message\");\r\n```\r\n\r\n## Installation\r\n\r\n```bash\r\n$ npm install @prftesp/cle-logger\r\n```\r\n\r\n## Features\r\n\r\n- Runs in Node.js and the browser\r\n- Remote logging\r\n- Multiple custom log sinks\r\n- Remote minimum log level overrides\r\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\r\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\r\n- Support for tagging logs with custom component names or correlation tokens\r\n- Send Twilio event hook data to a custom backend\r\n\r\n## Configuration\r\n\r\nThe logger can be used without configuration, and will by default log to console with a minimum logging level set to ```VERBOSE```.\r\n\r\nTo configure the logger call the ```init``` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\r\n\r\n**Available options**\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// Twilio account sid, required for use with CLE\r\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\r\n\r\n\t// Will be associated with all logs,\r\n\t// if not provided, a unique uuid will be generated\r\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\r\n\t// An array of custom log sinks that will receive an array of log objects,\r\n\t// and can optionally return a promise\r\n\tcustomLogSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: logs => console.log(logs),\r\n\t}],\r\n\r\n\t// A way to suppress error messages if set to false, defaults to true\r\n\tlogErrorsToConsole: true,\r\n\r\n\t// To support remote logging, add a url to receive logs\r\n\tlogUrl: \"https://example.com/api/logs\",\r\n\r\n\t// Will not send logs to the default remote log sink configured with 'logUrl'\r\n\tdisableRemoteLogging: false,\r\n\r\n\t// If logging Twilio Flex Actions, provide a list of actions \r\n\t// to be excluded from logs\r\n\texcludedFlexActionNames: [\"SendTyping\"],\r\n\r\n\t// Add console as a log sink, useful for debugging, defaults to true\r\n\tlogToConsole: true,\r\n\r\n\t// Provide a min log level, defaults to INFO\r\n\t// Use cleLogger.logLevels for predefined constants\r\n\tminLogLevel: cleLogger.logLevels.INFO,\r\n\r\n\t// A custom endpoint to fetch log level overrides\r\n\t// NOTE: the provided url should always end with 'accountSid=',\r\n\t// the library will append the accountSid\r\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\r\n\r\n\t// An instance of ('@twilio/flex-ui').Manager\r\n\ttwilioFlexManager: manager,\r\n\r\n\t// To send data from Twilio event hooks, to a custom backend,\r\n\t// configure an endpoint for each event type\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: 'https://example.com/twilio-task-router-hook?key=123',\r\n\t\tdebugger: 'https://example.com/twilio-debugger-hook?key=123',\r\n\t\tcallStatus: 'https://example.com/twilio-call-status-hook?key=123'\r\n\t}\r\n});\r\n```\r\n\r\n## API\r\n\r\nThe logger can be imported as an object in CommonJs or ES6 syntax:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n// or\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n```\r\n\r\nAlternatively, all top level members of the ```cleLogger``` are available as named exports and can be imported separately:\r\n\r\n```js\r\nimport { info, componentNames } from \"@prftesp/cle-logger\";\r\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\r\n\r\n// vs\r\n\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\r\n```\r\n\r\n### Logging levels\r\n\r\nLogging level constants can be found in ```cleLogger.logLevels```.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.logLevels.VERBOSE; // verbose\r\ncleLogger.logLevels.INFO; // info\r\ncleLogger.logLevels.WARN; // warn\r\ncleLogger.logLevels.ERROR; // error\r\ncleLogger.logLevels.CRITICAL; // critical\r\n```\r\n\r\nThere are functions available for each supported logging level:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(...);\r\ncleLogger.info(...);\r\ncleLogger.warn(...);\r\ncleLogger.error(...);\r\ncleLogger.critical(...);\r\n```\r\n\r\nThe functions are all async, return ```Promise<void>```, and can be optionally awaited. The suggested approach is to not await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is to suggested to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\r\n\r\n### Logging parameters\r\n\r\nAll logging functions take the same parameters:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(\r\n\t// The only required property, no logs will be generated without it\r\n\t\"Log message\",\r\n\r\n\t// Any additional log data you would like to include with the log,\r\n\t// can be any data type\r\n\t{ data: \"test\" },\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\t// CLE uses it to index these properties in Application Insights\r\n\t// For CLE, try to use keys found in 'cleLogger.customPropertyKeys'\r\n\t{ userId: 123 },\r\n\r\n\t// Another useful way to 'tag' your logs for easier searching\r\n\t// Can be any string, but for CLE, try to use available options in 'cleLogger.componentNames'\r\n\t// If you would like more component names added, please let us know \r\n\t\"ProfileComponent\",\r\n\r\n\t// Log code, can be any number used to categorize logs,\r\n\t// there will be a separate document listing CLE codes\r\n\t70123,\r\n\r\n\t// Correlation token to be associated with this log only,\r\n\t// if one is not provided, the token supplied/generated during 'init' will be used\r\n\t\"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\"\r\n);\r\n```\r\n\r\n### Available component names\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.componentNames.SESSION;\r\ncleLogger.componentNames.DIALER;\r\ncleLogger.componentNames.DASHBOARD;\r\ncleLogger.componentNames.AFTERCALL;\r\ncleLogger.componentNames.SUPERVISOR;\r\n```\r\n\r\n### Available custom property keys\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.customPropertyKeys.CHANNEL_SID;\r\ncleLogger.customPropertyKeys.TASK_SID;\r\ncleLogger.customPropertyKeys.CALL_SID;\r\ncleLogger.customPropertyKeys.WORKFLOW_SID;\r\ncleLogger.customPropertyKeys.WORKER_SID;\r\n```\r\n\r\n### Log codes\r\n\r\nTo categorize a log in one of the following categories, provide a log code in the appropriate range:\r\n\r\n- 100 000 - 109 999 => CALL_STATUS\r\n- 110 000 - 119 999 => IVR\r\n- 120 000 - 129 999 => ROUTING\r\n- 130 000 - 139 999 => AGENT_ACTION\r\n- 140 000 - 149 999 => TRANSFER\r\n- 150 000 - 159 999 => RM_INTEGRATION\r\n- 160 000 - 169 999 => WFM_INTEGRATION\r\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\r\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\r\n\r\n### Registering Twilio Actions\r\n\r\nExample in a Twilio Flex UI plugin:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\n/**\r\n * @param flex { typeof import('@twilio/flex-ui') }\r\n * @param manager { import('@twilio/flex-ui').Manager }\r\n */\r\ninit(flex, manager) {\r\n\tcleLogger.init({\r\n\t\taccountSid: \"your-account-sid\"\r\n\t});\r\n\r\n\tcleLogger.registerTwilioActions(flex);\r\n}\r\n```\r\n\r\n**Note**: A Twilio account sid must be provided during ```init``` with the ```accountSid``` parameter, otherwise the actions won't be logged.\r\n\r\nThe logger will loop through all actions found under ```flex.Actions.actions```, and attach ```after``` event handlers to log the action name and payload. Any private properties found in the payload (starting with '_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Any actions names configured in ```excludedFlexActionNames``` will not be included in the logs.\r\n\r\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\r\n\r\n**Twilio Actions log levels**\r\n\r\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of ```INFO```.\r\n\r\n``` js\r\nAcceptTask: INFO\r\nCancelTransfer: INFO\r\nCompleteTask: INFO\r\nHangupCall: INFO\r\nHideDirectory: VERBOSE\r\nHistoryGo: VERBOSE\r\nHistoryGoBack: VERBOSE\r\nHistoryGoForward: VERBOSE\r\nHistoryPush: VERBOSE\r\nHistoryReplace: VERBOSE\r\nHoldCall: INFO\r\nHoldParticipant: INFO\r\nKickParticipant: INFO\r\nLogout: INFO\r\nMonitorCall: INFO\r\nNavigateToView: INFO\r\nRejectTask: INFO\r\nSelectTask: INFO\r\nSelectTaskInSupervisor: INFO\r\nSelectWorkerInSupervisor: INFO\r\nSendMessage: INFO\r\nSendTyping: VERBOSE\r\nSetActivity: INFO\r\nSetInputText: VERBOSE\r\nShowDirectory: VERBOSE\r\nStopMonitoringCall: INFO\r\nToggleMute: INFO\r\nToggleSidebar: VERBOSE\r\nTransferTask: INFO\r\nUnholdCall: INFO\r\nUnholdParticipant: INFO\r\nWrapupTask: INFO\r\n```\r\n\r\n### Twilio Event Hooks\r\n\r\nCurrently, Twilio only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with ```twilioEventHooks``` option, and use the following functions to send event data to a custom endpoint.\r\n\r\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The data format should be the same as received from Twilio. The content type of data sent by the logger is ```application/x-www-form-urlencoded```.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// unrelated properties omitted\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: 'https://example.com/twilio-task-router-hook?key=123',\r\n\t\tdebugger: 'https://example.com/twilio-debugger-hook?key=123',\r\n\t\tcallStatus: 'https://example.com/twilio-call-status-hook?key=123'\r\n\t}\r\n});\r\n\r\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\r\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\r\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\r\n```\r\n\r\nExample Azure Function hook for debugger events:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\nmodule.exports = async function (context, req) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\tdebugger: 'https://example.com/twilio-debugger-hook?key=123',\r\n\t\t}\r\n\t});\r\n\r\n\t// log to console\r\n\tcleLogger.info(\"Received Twilio debugger event\");\r\n\t\r\n\t// send event data to a configured url\r\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\r\n};\r\n```\r\n\r\n### Version\r\n\r\nVersion will be automatically included in all logs.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.version; // 1.0.0\r\n```\r\n\r\n## Log Object\r\n\r\n```js\r\n{\r\n\t// Account sid if provided during init\r\n\taccountSid: string;\r\n\r\n\t// Any additional log data to be included with the log\r\n\tadditionalData: unknown;\r\n\t\r\n\t// Component name, if provided\r\n\tcomponentName: string;\r\n\r\n\t// Generated or configured token\r\n\tcorrelationToken: string;\r\n\r\n\t// Current UTC datetime in ISO 8601 format\r\n\teventDate: string;\r\n\r\n\t// Log level\r\n\tlevel: string;\r\n\r\n\t// Log code, if provided\r\n\tlogCode: number;\r\n\r\n\t// Log message\r\n\tmessage: string;\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\tproperties: object;\r\n\r\n\t// BrowserFunction if the environment is the browser\r\n\t// CloudFunction if the environment is Node.js\r\n\tsource: string;\r\n\r\n\t// Logging library version \r\n\tloggerVersion: string;\r\n}\r\n```\r\n\r\n## Advanced Configuration\r\n\r\n### Log sinks\r\n\r\nCustom log sinks can be provided via the ```customLogSinks``` config. It's an array of objects containing a ```name``` and a ```log``` method. All provided log sinks, along with the built-in remote and console log sinks will be executed in parallel. You can choose to await the logs to make sure all logging operations are completed.\r\nThe ```log``` method will receive an array of log objects for you to handle. If you're doing async requests in your custom log sinks, it is recommended to return the Promise.\r\n\r\nExample logging to file in Node.js:\r\n\r\n``` js\r\nconst fs = require(\"fs\");\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\ncleLogger.init({\r\n\tcustomLogSinks: [{\r\n\t\tname: \"testFileSink\",\r\n\t\tlog: logs => {\r\n\t\t\tfs.appendFile(\r\n\t\t\t\t\"./log.txt\",\r\n\t\t\t\t`${JSON.stringify(logs)}`,\r\n\t\t\t\tconsole.log\r\n\t\t\t);\r\n\t\t}\r\n\t}]\r\n});\r\n```\r\n\r\n### Remote logging\r\n\r\nThe library provides a way to provide you own remote log url, or to remotely override the min log level. This can be useful for temporarily setting the log level to verbose in a production environment, for quick troubleshooting.\r\n\r\n**Remote log endpoint**\r\n\r\nTo implement your own remote logging endpoint, all you have to do is accept ```POST``` requests, with a JSON payload of an array of [log objects](#log-object), and return either ```{ success: true }``` or ```{ success: false }```. The endpoint should always return a ```200 OK``` status code. A good use-case would be leveraging a serverless api platform to output logs to a queue.\r\n\r\nExample Azure Function:\r\n\r\n```js\r\nmodule.exports = async function (context, req) {\r\n    context.log(req.body); // body will have the log array\r\n\r\n    context.res = {\r\n        status: 200,\r\n        body: { success: true }\r\n    };\r\n};\r\n```\r\n\r\n**Remote log level override**\r\n\r\nThe ```minLogLevelUrl``` configuration property is used to provide an endpoint that accepts ```GET``` requests and a ```accountSid``` query string parameter. ```accountSid``` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the ```minLogLevel``` configuration property. If null is returned, the ```minLogLevel``` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a ```200 OK``` status code and use the ```success``` property to indicate failure.\r\n\r\nExample success response:\r\n\r\n```json\r\n{ \r\n\t\"success\": true,\r\n\t\"minLogLevel\": \"warn\"\r\n}\r\n```\r\n\r\nExample error response:\r\n\r\n```json\r\n{ \r\n\t\"success\": false,\r\n\t\"minLogLevel\": null\r\n}\r\n```\r\n\r\n## Changelog\r\n\r\n### v1.0.0 [2019-12-09]\r\n\r\n- Initial release","readmeFilename":"README.md","_id":"@prftesp/cle-logger@1.0.0-beta.1","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-MUmHffk0QMAx2/SeERskPEFPZg2EwaMal2ysDw2w1GwbguZDnJM/nKneAldq0XxGkkTr2SzOd9MwWDwYaKhPNw==","shasum":"2709499e953160503364679ce5cc0f3ab678986a","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-1.0.0-beta.1.tgz","fileCount":17,"unpackedSize":56121,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdeoFICRA9TVsSAnZWagAAqvgP/A1uvyPC0fDHTJv5ZxKi\ntWhju6hav97KxXvK9pz/Cuuy6EkwwNV/vXxRuCN+sj0NtaptnLqIwvDZeOe8\nvZmM1t96psHyO5DD/yJ/DiWpAQB+fjIWbRcHvtdS+pQFQE8CsQ69QXQmKVM3\nETaS6M++Y2xXyrrZCZcz9h84oSwzz4E4KnFhc5tBokdyTigps9jLrxEhIzCW\n9YwGgv+5hPs184GYSzZNv8xNkIv92NCC1kwdqX/jnwvyGofC0crjdJbcfpRQ\nABZuHW27yKvGVCQupXWajTNWEytozLj0xYUcSqklhoac0HYJ3ROBfpXf1IhM\nDmgbpC6SGrxs3spNVCMIhoQ3djYZhj03l6vrag81wNCYwjcAbeZxqv7UHOsS\neSq1qP/3RaOn0KpcxBlAmPP67aATjfebffCCezG+noJQ9jW4r2wfE8b5fhcE\nNFmxqevZW1rD5HIWbFLsgyyXZvskCSU5M1lqla6lt194IVIAg3N9oBreaqqv\nEiXfqjjnUc9PmGiXPwRxf74Dz6ZvqZpZVdJ+srasR+0LB7JweRjuUYc+MHz6\n3qGTAFFQxpeNpKkujgxFI7rxt4xN3FmnScMk2dwIWrRMNF1+py3mqvddbaUH\ni2BhLTDEC4FesTpAcoxVlvNwxZsyyuZGG+aHAZNGLS/nQLBOF1agW1VijBGB\nb9tq\r\n=SfWb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBxRwyhqjkggAARz0CJ2t1f7/dOlRLLxtS2Ehz4ktsTCAiBwvZIUzllu01Qzbv6Hrs5gEOWWxqFBL2qsCJ3nNZ+OCQ=="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_1.0.0-beta.1_1568309575520_0.38601235899722086"},"_hasShrinkwrap":false},"1.0.0-beta.2":{"name":"@prftesp/cle-logger","version":"1.0.0-beta.2","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿CLE Logger\r\n===\r\n\r\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\r\n\r\n## Documentation\r\n\r\n- [Target Environment](#target-environment)\r\n- [Simple Example](#simple-example)\r\n- [Installation](#installation)\r\n- [Features](#features)\r\n- [Configuration](#configuration)\r\n- [API](#api)\r\n\t- [Logging levels](#logging-levels)\r\n\t- [Logging parameters](#logging-parameters)\r\n\t- [Available component names](#available-component-names)\r\n\t- [Available custom property keys](#available-custom-property-keys)\r\n\t- [Log codes](#log-codes)\r\n\t- [Registering Twilio Actions](#registering-twilio-actions)\r\n\t- [Twilio Event Hooks](#twilio-event-hooks)\r\n\t- [Version](#version)\r\n- [Log Object](#log-object)\r\n- [Advanced Configuration](#advanced-configuration)\r\n\t- [Log sinks](#log-sinks)\r\n\t- [Remote logging](#remote-logging)\r\n- [Changelog](#changelog)\r\n\r\n## Target Environment\r\n\r\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\r\n\r\n## Simple Example\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\n// will log message to console\r\ncleLogger.info(\"log message\");\r\n```\r\n\r\n## Installation\r\n\r\n```bash\r\n$ npm install @prftesp/cle-logger\r\n```\r\n\r\n## Features\r\n\r\n- Runs in Node.js and the browser\r\n- Remote logging\r\n- Multiple custom log sinks\r\n- Remote minimum log level overrides\r\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\r\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\r\n- Support for tagging logs with custom component names or correlation tokens\r\n- Send Twilio event hook data to a custom backend\r\n\r\n## Configuration\r\n\r\nThe logger can be used without configuration, and will by default log to console with a minimum logging level set to ```VERBOSE```.\r\n\r\nTo configure the logger call the ```init``` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\r\n\r\n**Available options**\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// Twilio account sid, required for use with CLE\r\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\r\n\r\n\t// Will be associated with all logs,\r\n\t// if not provided, a unique uuid will be generated\r\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\r\n\t// An array of custom log sinks that will receive an array of log objects,\r\n\t// and can optionally return a promise\r\n\tcustomLogSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: logs => console.log(logs),\r\n\t}],\r\n\r\n\t// A way to suppress error messages if set to false, defaults to true\r\n\tlogErrorsToConsole: true,\r\n\r\n\t// To support remote logging, add a url to receive logs\r\n\tlogUrl: \"https://example.com/api/logs\",\r\n\r\n\t// Will not send logs to the default remote log sink configured with 'logUrl'\r\n\tdisableRemoteLogging: false,\r\n\r\n\t// If logging Twilio Flex Actions, provide a list of actions \r\n\t// to be excluded from logs\r\n\texcludedFlexActionNames: [\"SendTyping\"],\r\n\r\n\t// Add console as a log sink, useful for debugging, defaults to true\r\n\tlogToConsole: true,\r\n\r\n\t// Provide a min log level, defaults to INFO\r\n\t// Use cleLogger.logLevels for predefined constants\r\n\tminLogLevel: cleLogger.logLevels.INFO,\r\n\r\n\t// A custom endpoint to fetch log level overrides\r\n\t// NOTE: the provided url should always end with 'accountSid=',\r\n\t// the library will append the accountSid\r\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\r\n\r\n\t// An instance of ('@twilio/flex-ui').Manager\r\n\ttwilioFlexManager: manager,\r\n\r\n\t// To send data from Twilio event hooks, to a custom backend,\r\n\t// configure one or more endpoints for each event type\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\r\n\t}\r\n});\r\n```\r\n\r\n## API\r\n\r\nThe logger can be imported as an object in CommonJs or ES6 syntax:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n// or\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n```\r\n\r\nAlternatively, all top level members of the ```cleLogger``` are available as named exports and can be imported separately:\r\n\r\n```js\r\nimport { info, componentNames } from \"@prftesp/cle-logger\";\r\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\r\n\r\n// vs\r\n\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\r\n```\r\n\r\n### Logging levels\r\n\r\nLogging level constants can be found in ```cleLogger.logLevels```.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.logLevels.VERBOSE; // verbose\r\ncleLogger.logLevels.INFO; // info\r\ncleLogger.logLevels.WARN; // warn\r\ncleLogger.logLevels.ERROR; // error\r\ncleLogger.logLevels.CRITICAL; // critical\r\n```\r\n\r\nThere are functions available for each supported logging level:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(...);\r\ncleLogger.info(...);\r\ncleLogger.warn(...);\r\ncleLogger.error(...);\r\ncleLogger.critical(...);\r\n```\r\n\r\nThe functions are all async, return ```Promise<void>```, and can be optionally awaited. The suggested approach is to not await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is to suggested to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\r\n\r\n### Logging parameters\r\n\r\nAll logging functions take the same parameters:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(\r\n\t// The only required property, no logs will be generated without it\r\n\t\"Log message\",\r\n\r\n\t// Any additional log data you would like to include with the log,\r\n\t// can be any data type\r\n\t{ data: \"test\" },\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\t// CLE uses it to index these properties in Application Insights\r\n\t// For CLE, try to use keys found in 'cleLogger.customPropertyKeys'\r\n\t{ userId: 123 },\r\n\r\n\t// Another useful way to 'tag' your logs for easier searching\r\n\t// Can be any string, but for CLE, try to use available options in 'cleLogger.componentNames'\r\n\t// If you would like more component names added, please let us know \r\n\t\"ProfileComponent\",\r\n\r\n\t// Log code, can be any number used to categorize logs,\r\n\t// there will be a separate document listing CLE codes\r\n\t70123,\r\n\r\n\t// Correlation token to be associated with this log only,\r\n\t// if one is not provided, the token supplied/generated during 'init' will be used\r\n\t\"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\"\r\n);\r\n```\r\n\r\n### Available component names\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.componentNames.SESSION;\r\ncleLogger.componentNames.DIALER;\r\ncleLogger.componentNames.DASHBOARD;\r\ncleLogger.componentNames.AFTERCALL;\r\ncleLogger.componentNames.SUPERVISOR;\r\n```\r\n\r\n### Available custom property keys\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.customPropertyKeys.CHANNEL_SID;\r\ncleLogger.customPropertyKeys.TASK_SID;\r\ncleLogger.customPropertyKeys.CALL_SID;\r\ncleLogger.customPropertyKeys.WORKFLOW_SID;\r\ncleLogger.customPropertyKeys.WORKER_SID;\r\n```\r\n\r\n### Log codes\r\n\r\nTo categorize a log in one of the following categories, provide a log code in the appropriate range:\r\n\r\n- 100 000 - 109 999 => CALL_STATUS\r\n- 110 000 - 119 999 => IVR\r\n- 120 000 - 129 999 => ROUTING\r\n- 130 000 - 139 999 => AGENT_ACTION\r\n- 140 000 - 149 999 => TRANSFER\r\n- 150 000 - 159 999 => RM_INTEGRATION\r\n- 160 000 - 169 999 => WFM_INTEGRATION\r\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\r\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\r\n\r\n### Registering Twilio Actions\r\n\r\nExample in a Twilio Flex UI plugin:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\n/**\r\n * @param flex { typeof import('@twilio/flex-ui') }\r\n * @param manager { import('@twilio/flex-ui').Manager }\r\n */\r\ninit(flex, manager) {\r\n\tcleLogger.init({\r\n\t\taccountSid: \"your-account-sid\"\r\n\t});\r\n\r\n\tcleLogger.registerTwilioActions(flex);\r\n}\r\n```\r\n\r\n**Note**: A Twilio account sid must be provided during ```init``` with the ```accountSid``` parameter, otherwise the actions won't be logged.\r\n\r\nThe logger will loop through all actions found under ```flex.Actions.actions```, and attach ```after``` event handlers to log the action name and payload. Any private properties found in the payload (starting with '_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Any actions names configured in ```excludedFlexActionNames``` will not be included in the logs.\r\n\r\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\r\n\r\n**Twilio Actions log levels**\r\n\r\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of ```INFO```.\r\n\r\n``` js\r\nAcceptTask: INFO\r\nCancelTransfer: INFO\r\nCompleteTask: INFO\r\nHangupCall: INFO\r\nHideDirectory: VERBOSE\r\nHistoryGo: VERBOSE\r\nHistoryGoBack: VERBOSE\r\nHistoryGoForward: VERBOSE\r\nHistoryPush: VERBOSE\r\nHistoryReplace: VERBOSE\r\nHoldCall: INFO\r\nHoldParticipant: INFO\r\nKickParticipant: INFO\r\nLogout: INFO\r\nMonitorCall: INFO\r\nNavigateToView: INFO\r\nRejectTask: INFO\r\nSelectTask: INFO\r\nSelectTaskInSupervisor: INFO\r\nSelectWorkerInSupervisor: INFO\r\nSendMessage: INFO\r\nSendTyping: VERBOSE\r\nSetActivity: INFO\r\nSetInputText: VERBOSE\r\nShowDirectory: VERBOSE\r\nStopMonitoringCall: INFO\r\nToggleMute: INFO\r\nToggleSidebar: VERBOSE\r\nTransferTask: INFO\r\nUnholdCall: INFO\r\nUnholdParticipant: INFO\r\nWrapupTask: INFO\r\n```\r\n\r\n### Twilio Event Hooks\r\n\r\nCurrently, Twilio only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with ```twilioEventHooks``` option, and use the following functions to send event data to a custom endpoint.\r\n\r\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The data format should be the same as received from Twilio. The content type of data sent by the logger is ```application/x-www-form-urlencoded```.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// unrelated properties omitted\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\r\n\t}\r\n});\r\n\r\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\r\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\r\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\r\n```\r\n\r\nExample Azure Function hook for debugger events:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\nmodule.exports = async function (context, req) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\t}\r\n\t});\r\n\r\n\t// log to console\r\n\tcleLogger.info(\"Received Twilio debugger event\");\r\n\t\r\n\t// send event data to a configured url\r\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\r\n};\r\n```\r\n\r\n### Version\r\n\r\nVersion will be automatically included in all logs.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.version; // 1.0.0\r\n```\r\n\r\n## Log Object\r\n\r\n```js\r\n{\r\n\t// Account sid if provided during init\r\n\taccountSid: string;\r\n\r\n\t// Any additional log data to be included with the log\r\n\tadditionalData: unknown;\r\n\t\r\n\t// Component name, if provided\r\n\tcomponentName: string;\r\n\r\n\t// Generated or configured token\r\n\tcorrelationToken: string;\r\n\r\n\t// Current UTC datetime in ISO 8601 format\r\n\teventDate: string;\r\n\r\n\t// Log level\r\n\tlevel: string;\r\n\r\n\t// Log code, if provided\r\n\tlogCode: number;\r\n\r\n\t// Log message\r\n\tmessage: string;\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\tproperties: object;\r\n\r\n\t// BrowserFunction if the environment is the browser\r\n\t// CloudFunction if the environment is Node.js\r\n\tsource: string;\r\n\r\n\t// Logging library version \r\n\tloggerVersion: string;\r\n}\r\n```\r\n\r\n## Advanced Configuration\r\n\r\n### Log sinks\r\n\r\nCustom log sinks can be provided via the ```customLogSinks``` config. It's an array of objects containing a ```name``` and a ```log``` method. All provided log sinks, along with the built-in remote and console log sinks will be executed in parallel. You can choose to await the logs to make sure all logging operations are completed.\r\nThe ```log``` method will receive an array of log objects for you to handle. If you're doing async requests in your custom log sinks, it is recommended to return the Promise.\r\n\r\nExample logging to file in Node.js:\r\n\r\n``` js\r\nconst fs = require(\"fs\");\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\ncleLogger.init({\r\n\tcustomLogSinks: [{\r\n\t\tname: \"testFileSink\",\r\n\t\tlog: logs => {\r\n\t\t\tfs.appendFile(\r\n\t\t\t\t\"./log.txt\",\r\n\t\t\t\t`${JSON.stringify(logs)}`,\r\n\t\t\t\tconsole.log\r\n\t\t\t);\r\n\t\t}\r\n\t}]\r\n});\r\n```\r\n\r\n### Remote logging\r\n\r\nThe library provides a way to provide you own remote log url, or to remotely override the min log level. This can be useful for temporarily setting the log level to verbose in a production environment, for quick troubleshooting.\r\n\r\n**Remote log endpoint**\r\n\r\nTo implement your own remote logging endpoint, all you have to do is accept ```POST``` requests, with a JSON payload of an array of [log objects](#log-object), and return either ```{ success: true }``` or ```{ success: false }```. The endpoint should always return a ```200 OK``` status code. A good use-case would be leveraging a serverless api platform to output logs to a queue.\r\n\r\nExample Azure Function:\r\n\r\n```js\r\nmodule.exports = async function (context, req) {\r\n    context.log(req.body); // body will have the log array\r\n\r\n    context.res = {\r\n        status: 200,\r\n        body: { success: true }\r\n    };\r\n};\r\n```\r\n\r\n**Remote log level override**\r\n\r\nThe ```minLogLevelUrl``` configuration property is used to provide an endpoint that accepts ```GET``` requests and a ```accountSid``` query string parameter. ```accountSid``` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the ```minLogLevel``` configuration property. If null is returned, the ```minLogLevel``` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a ```200 OK``` status code and use the ```success``` property to indicate failure.\r\n\r\nExample success response:\r\n\r\n```json\r\n{ \r\n\t\"success\": true,\r\n\t\"minLogLevel\": \"warn\"\r\n}\r\n```\r\n\r\nExample error response:\r\n\r\n```json\r\n{ \r\n\t\"success\": false,\r\n\t\"minLogLevel\": null\r\n}\r\n```\r\n\r\n## Changelog\r\n\r\n### v1.0.0 [2019-12-09]\r\n\r\n- Initial release","readmeFilename":"README.md","_id":"@prftesp/cle-logger@1.0.0-beta.2","_npmVersion":"6.4.1","_nodeVersion":"10.15.2","_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"dist":{"integrity":"sha512-S2T34DcslxFJmCx0PmrwkqT0F51Bnu+YNX+KRO1QnqYkGB9JU6bZt/vPHGO1yFRsTN2mRTDVMFkuGcWHBv1h1Q==","shasum":"e93f66a3ee1fd46acedd9724e090a723694bbd77","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-1.0.0-beta.2.tgz","fileCount":17,"unpackedSize":56908,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdeoZaCRA9TVsSAnZWagAAbAMQAIhI3dG6A1knOLrCM7BO\noMv/xanJjxBjpO1BH5N47qhG6XAqGnx7XoZH6gVtthHCzbyp2SQyErFgvTOD\n6zm3vsJSlLTKcT73lNsp/YAJewJaUMHI2qweyLqozGNg9mp6wJYWZoFW2+9v\nThf20d8Z/tMaJtu4/JvVoBfmV61vNUOmJXhj/KUSkQwnUmGiw8zvuha7dAqb\nlsgQ4CLKy0fahSYNF+rijmN+0irs5YURqHId5geXzlDegvE+YsMqM1iFi1DC\nWR2onmvnuX4Icna7o54u/ViLEE0Oa2m+k8vDL2uCNG8Tk7qFWvEfCI4am2v2\nbLMsA0X/X4TGUjWI2gWcYY/y73wxuzHV/iESLugULC8YGL/Uuh+5JWLKvDeR\nnaZ47xgk0uIZbFKuF3w+5psu+7nbLmW8Urr6SLtXQBs47A26u9mhUBtGXRcV\ngB1OykoZw1BWOCAI6h9pQHZGlsSHeqmhLELVDCkxpR4F1H+/FzYW/eeqGfRR\ngWNZEYCK6zU2Li6s7wjhsMsrJ2HwKeq08TU4vNkx5074ialKysMN/9jej3t2\nWA2qjkNKWpUI7qdt/GB/53a5pKBpLPL6cc01JHIbolMvHrMztxko7QRa54ER\ncfYcYQPm+a9yg2Ugztmnb0rtuRlihXFlHagAYH1lWWLE3zlT5/UTaDxxcCgM\nQlX8\r\n=9zYM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAQ7qpYYjt8BlhUicbMLgr6WKPCXDH28rBuQtNqSZw2rAiBAUgIUCjpe1QRPKbPoqA7NCc+/R/8ggTOUuZKnJBClvg=="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_1.0.0-beta.2_1568310873776_0.882915560771516"},"_hasShrinkwrap":false},"1.0.0-beta.20190918.4":{"name":"@prftesp/cle-logger","version":"1.0.0-beta.20190918.4","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿CLE Logger\n===\n\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\n\n## Documentation\n\n- [Target Environment](#target-environment)\n- [Simple Example](#simple-example)\n- [Installation](#installation)\n- [Features](#features)\n- [Configuration](#configuration)\n- [API](#api)\n\t- [Logging levels](#logging-levels)\n\t- [Logging parameters](#logging-parameters)\n\t- [Available component names](#available-component-names)\n\t- [Available custom property keys](#available-custom-property-keys)\n\t- [Log codes](#log-codes)\n\t- [Registering Twilio Actions](#registering-twilio-actions)\n\t- [Twilio Event Hooks](#twilio-event-hooks)\n\t- [Version](#version)\n- [Log Object](#log-object)\n- [Advanced Configuration](#advanced-configuration)\n\t- [Log sinks](#log-sinks)\n\t- [Remote logging](#remote-logging)\n- [Changelog](#changelog)\n\n## Target Environment\n\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\n\n## Simple Example\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n// will log message to console\ncleLogger.info(\"log message\");\n```\n\n## Installation\n\n```bash\n$ npm install @prftesp/cle-logger\n```\n\n## Features\n\n- Runs in Node.js and the browser\n- Remote logging\n- Multiple custom log sinks\n- Remote minimum log level overrides\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\n- Support for tagging logs with custom component names or correlation tokens\n- Send Twilio event hook data to a custom backend\n\n## Configuration\n\nThe logger can be used without configuration, and will by default log to console with a minimum logging level set to ```VERBOSE```.\n\nTo configure the logger call the ```init``` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\n\n**Available options**\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// Twilio account sid, required for use with CLE\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\n\n\t// Will be associated with all logs,\n\t// if not provided, a unique uuid will be generated\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\n\n\t// An array of custom log sinks that will receive an array of log objects,\n\t// and can optionally return a promise\n\tcustomLogSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: logs => console.log(logs),\n\t}],\n\n\t// A way to suppress error messages if set to false, defaults to true\n\tlogErrorsToConsole: true,\n\n\t// To support remote logging, add a url to receive logs\n\tlogUrl: \"https://example.com/api/logs\",\n\n\t// Will not send logs to the default remote log sink configured with 'logUrl'\n\tdisableRemoteLogging: false,\n\n\t// If logging Twilio Flex Actions, provide a list of actions \n\t// to be excluded from logs\n\texcludedFlexActionNames: [\"SendTyping\"],\n\n\t// Add console as a log sink, useful for debugging, defaults to true\n\tlogToConsole: true,\n\n\t// Provide a min log level, defaults to INFO\n\t// Use cleLogger.logLevels for predefined constants\n\tminLogLevel: cleLogger.logLevels.INFO,\n\n\t// A custom endpoint to fetch log level overrides\n\t// NOTE: the provided url should always end with 'accountSid=',\n\t// the library will append the accountSid\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\n\n\t// An instance of ('@twilio/flex-ui').Manager\n\ttwilioFlexManager: manager,\n\n\t// To send data from Twilio event hooks, to a custom backend,\n\t// configure one or more endpoints for each event type\n\ttwilioEventHooks: {\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\n\t}\n});\n```\n\n## API\n\nThe logger can be imported as an object in CommonJs or ES6 syntax:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n// or\nconst cleLogger = require(\"@prftesp/cle-logger\");\n```\n\nAlternatively, all top level members of the ```cleLogger``` are available as named exports and can be imported separately:\n\n```js\nimport { info, componentNames } from \"@prftesp/cle-logger\";\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\n\n// vs\n\nimport { cleLogger } from \"@prftesp/cle-logger\";\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\n```\n\n### Logging levels\n\nLogging level constants can be found in ```cleLogger.logLevels```.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.logLevels.VERBOSE; // verbose\ncleLogger.logLevels.INFO; // info\ncleLogger.logLevels.WARN; // warn\ncleLogger.logLevels.ERROR; // error\ncleLogger.logLevels.CRITICAL; // critical\n```\n\nThere are functions available for each supported logging level:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(...);\ncleLogger.info(...);\ncleLogger.warn(...);\ncleLogger.error(...);\ncleLogger.critical(...);\n```\n\nThe functions are all async, return ```Promise<void>```, and can be optionally awaited. The suggested approach is to not await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is to suggested to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\n\n### Logging parameters\n\nAll logging functions take the same parameters:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(\n\t// The only required property, no logs will be generated without it\n\t\"Log message\",\n\n\t// Any additional log data you would like to include with the log,\n\t// can be any data type\n\t{ data: \"test\" },\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\t// CLE uses it to index these properties in Application Insights\n\t// For CLE, try to use keys found in 'cleLogger.customPropertyKeys'\n\t{ userId: 123 },\n\n\t// Another useful way to 'tag' your logs for easier searching\n\t// Can be any string, but for CLE, try to use available options in 'cleLogger.componentNames'\n\t// If you would like more component names added, please let us know \n\t\"ProfileComponent\",\n\n\t// Log code, can be any number used to categorize logs,\n\t// there will be a separate document listing CLE codes\n\t70123,\n\n\t// Correlation token to be associated with this log only,\n\t// if one is not provided, the token supplied/generated during 'init' will be used\n\t\"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\"\n);\n```\n\n### Available component names\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.componentNames.SESSION;\ncleLogger.componentNames.DIALER;\ncleLogger.componentNames.DASHBOARD;\ncleLogger.componentNames.AFTERCALL;\ncleLogger.componentNames.SUPERVISOR;\n```\n\n### Available custom property keys\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.customPropertyKeys.CHANNEL_SID;\ncleLogger.customPropertyKeys.TASK_SID;\ncleLogger.customPropertyKeys.CALL_SID;\ncleLogger.customPropertyKeys.WORKFLOW_SID;\ncleLogger.customPropertyKeys.WORKER_SID;\n```\n\n### Log codes\n\nTo categorize a log in one of the following categories, provide a log code in the appropriate range:\n\n- 100 000 - 109 999 => CALL_STATUS\n- 110 000 - 119 999 => IVR\n- 120 000 - 129 999 => ROUTING\n- 130 000 - 139 999 => AGENT_ACTION\n- 140 000 - 149 999 => TRANSFER\n- 150 000 - 159 999 => RM_INTEGRATION\n- 160 000 - 169 999 => WFM_INTEGRATION\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\n\n### Registering Twilio Actions\n\nExample in a Twilio Flex UI plugin:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n/**\n * @param flex { typeof import('@twilio/flex-ui') }\n * @param manager { import('@twilio/flex-ui').Manager }\n */\ninit(flex, manager) {\n\tcleLogger.init({\n\t\taccountSid: \"your-account-sid\"\n\t});\n\n\tcleLogger.registerTwilioActions(flex);\n}\n```\n\n**Note**: A Twilio account sid must be provided during ```init``` with the ```accountSid``` parameter, otherwise the actions won't be logged.\n\nThe logger will loop through all actions found under ```flex.Actions.actions```, and attach ```after``` event handlers to log the action name and payload. Any private properties found in the payload (starting with '_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Any actions names configured in ```excludedFlexActionNames``` will not be included in the logs.\n\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\n\n**Twilio Actions log levels**\n\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of ```INFO```.\n\n``` js\nAcceptTask: INFO\nCancelTransfer: INFO\nCompleteTask: INFO\nHangupCall: INFO\nHideDirectory: VERBOSE\nHistoryGo: VERBOSE\nHistoryGoBack: VERBOSE\nHistoryGoForward: VERBOSE\nHistoryPush: VERBOSE\nHistoryReplace: VERBOSE\nHoldCall: INFO\nHoldParticipant: INFO\nKickParticipant: INFO\nLogout: INFO\nMonitorCall: INFO\nNavigateToView: INFO\nRejectTask: INFO\nSelectTask: INFO\nSelectTaskInSupervisor: INFO\nSelectWorkerInSupervisor: INFO\nSendMessage: INFO\nSendTyping: VERBOSE\nSetActivity: INFO\nSetInputText: VERBOSE\nShowDirectory: VERBOSE\nStopMonitoringCall: INFO\nToggleMute: INFO\nToggleSidebar: VERBOSE\nTransferTask: INFO\nUnholdCall: INFO\nUnholdParticipant: INFO\nWrapupTask: INFO\n```\n\n### Twilio Event Hooks\n\nCurrently, Twilio only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with ```twilioEventHooks``` option, and use the following functions to send event data to a custom endpoint.\n\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The data format should be the same as received from Twilio. The content type of data sent by the logger is ```application/x-www-form-urlencoded```.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// unrelated properties omitted\n\ttwilioEventHooks: {\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\n\t}\n});\n\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\n```\n\nExample Azure Function hook for debugger events:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\nmodule.exports = async function (context, req) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\n\t\t}\n\t});\n\n\t// log to console\n\tcleLogger.info(\"Received Twilio debugger event\");\n\t\n\t// send event data to a configured url\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\n};\n```\n\n### Version\n\nVersion will be automatically included in all logs.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.version; // 1.0.0\n```\n\n## Log Object\n\n```js\n{\n\t// Account sid if provided during init\n\taccountSid: string;\n\n\t// Any additional log data to be included with the log\n\tadditionalData: unknown;\n\t\n\t// Component name, if provided\n\tcomponentName: string;\n\n\t// Generated or configured token\n\tcorrelationToken: string;\n\n\t// Current UTC datetime in ISO 8601 format\n\teventDate: string;\n\n\t// Log level\n\tlevel: string;\n\n\t// Log code, if provided\n\tlogCode: number;\n\n\t// Log message\n\tmessage: string;\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\tproperties: object;\n\n\t// BrowserFunction if the environment is the browser\n\t// CloudFunction if the environment is Node.js\n\tsource: string;\n\n\t// Logging library version \n\tloggerVersion: string;\n}\n```\n\n## Advanced Configuration\n\n### Log sinks\n\nCustom log sinks can be provided via the ```customLogSinks``` config. It's an array of objects containing a ```name``` and a ```log``` method. All provided log sinks, along with the built-in remote and console log sinks will be executed in parallel. You can choose to await the logs to make sure all logging operations are completed.\nThe ```log``` method will receive an array of log objects for you to handle. If you're doing async requests in your custom log sinks, it is recommended to return the Promise.\n\nExample logging to file in Node.js:\n\n``` js\nconst fs = require(\"fs\");\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\ncleLogger.init({\n\tcustomLogSinks: [{\n\t\tname: \"testFileSink\",\n\t\tlog: logs => {\n\t\t\tfs.appendFile(\n\t\t\t\t\"./log.txt\",\n\t\t\t\t`${JSON.stringify(logs)}`,\n\t\t\t\tconsole.log\n\t\t\t);\n\t\t}\n\t}]\n});\n```\n\n### Remote logging\n\nThe library provides a way to provide you own remote log url, or to remotely override the min log level. This can be useful for temporarily setting the log level to verbose in a production environment, for quick troubleshooting.\n\n**Remote log endpoint**\n\nTo implement your own remote logging endpoint, all you have to do is accept ```POST``` requests, with a JSON payload of an array of [log objects](#log-object), and return either ```{ success: true }``` or ```{ success: false }```. The endpoint should always return a ```200 OK``` status code. A good use-case would be leveraging a serverless api platform to output logs to a queue.\n\nExample Azure Function:\n\n```js\nmodule.exports = async function (context, req) {\n    context.log(req.body); // body will have the log array\n\n    context.res = {\n        status: 200,\n        body: { success: true }\n    };\n};\n```\n\n**Remote log level override**\n\nThe ```minLogLevelUrl``` configuration property is used to provide an endpoint that accepts ```GET``` requests and a ```accountSid``` query string parameter. ```accountSid``` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the ```minLogLevel``` configuration property. If null is returned, the ```minLogLevel``` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a ```200 OK``` status code and use the ```success``` property to indicate failure.\n\nExample success response:\n\n```json\n{ \n\t\"success\": true,\n\t\"minLogLevel\": \"warn\"\n}\n```\n\nExample error response:\n\n```json\n{ \n\t\"success\": false,\n\t\"minLogLevel\": null\n}\n```\n\n## Changelog\n\n### v1.0.0 [2019-12-09]\n\n- Initial release","readmeFilename":"README.md","_id":"@prftesp/cle-logger@1.0.0-beta.20190918.4","_nodeVersion":"10.16.3","_npmVersion":"6.11.3","dist":{"integrity":"sha512-BGooAdbOPSYxnF14+1d/W97swAUpvovDe5iIJeX2IOiSPEqUhHJ/l6bF9TNbjNBw/LRtGKS1wlgXs3yK2j/g3g==","shasum":"766e90e806c26c91e3e17b208ea556b24fb6061b","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-1.0.0-beta.20190918.4.tgz","fileCount":17,"unpackedSize":55728,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdglJzCRA9TVsSAnZWagAAbRQP/itTajmgIGGArU21z7yD\n1j183YALe+INlg5LlfMdNmT8wfTgdMqmKZVBw0HUVIWyFPeIrMHKb6fRwU5A\nWF3IIYhHUR15Ki84AvXSi4i08pH3nfl4DKARGfyllqXOr+TWl+Vgwg+yslC2\nA27naIvrk2acM7t/EI4s5ZxHseIBlD0r1WWTke2aF2XCpzyiBH8M43ITTA73\nvL5pPNH+86RBq6vCjgM+UacO8ZjU6sWGadmMjrdrVPcN4Kfo24/hBJOac6CH\n1xfKRtURArWhUPK1c/IvBXJd6iOiSwDqpl028Y4jsEpGSEWgpuQTkYfqNOn0\nLPZpbH4QrgC0U2UAb00JrUcbuDFTihn6c4pB4wDaR8ukKfO9RvdgfeIGYMLQ\nqdbFBP6tVIjK6Yeh8EmGW69cn8V9cpaYEbdRrNLrxQEkPUqo4P4xZvJi4cSa\n98EHFMsZ97tDe8V4x80jwPZZ9xKkOz29ZlXA8fndlHfVbbAXTJpmi22pW/vE\n1fOmI1dNmGclVNPTMWGfOwt9JjnRBN6g+2bmGdr5UCal/oDHQT/XYgz58AdB\ndzhJANaFnAbfDooayO3QY0FEWl2qgiDANzFTCS/jeuXmHSNywq0MuaFkvY2M\nS2u26fAD1WE54yZkVH/zejM7yYMwss9ddxMzTmY9M780H7ngt8XmwljVzfXa\nIJpf\r\n=Jh0G\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCFnESarwPuFJkYA7GrNTO2daxSAgEu/ostoa0Av1KibwIgWgV+QPX5w8uTeDf+C3Nn71i1YVRmE7UhweXvK/9lx7k="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_1.0.0-beta.20190918.4_1568821874907_0.027884545070949285"},"_hasShrinkwrap":false},"1.0.0":{"name":"@prftesp/cle-logger","version":"1.0.0","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@1.0.0","_nodeVersion":"10.16.3","_npmVersion":"6.11.3","dist":{"integrity":"sha512-Z75keidNFbPVIIaP0ClGP7u0eQSryd3F+tYOhYYd7ZlqbWkk1tz1GS3yXac3qkoZaLajKGr26x1Ve09EeFnXeQ==","shasum":"ae857e6c16a50f9cd1b846408adb8c75f24dc5e7","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-1.0.0.tgz","fileCount":17,"unpackedSize":55629,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdglXCCRA9TVsSAnZWagAA0M8P/RjnRW547TbNp7WENut4\nkkiB2XzfDH3ZghADul0i5UtIGhmiLbF1TWgacR6xPUh9201KARWjGFeC9/Lu\n3HxhyxfNxnVEO3KfflNT0IIr5lqVwP3HfbOf/MmPkTG7DODpzilnxFdIOB2i\nOPdPzM23JH73CIjC2pO+Ba49B/DQivMi6YX0an4WGD61KNdiJv/Srm68jzeS\nPSi5V+HTFLg2hR/Mm3837ySx2c56kVYzEg19XjPx5f6UJbhazP4qOKITWsPk\nyzkeY1yjA9saia4xp+A+xLdcdaFt9rZGLEHlHOnf2Mq08OYswQb6BNRxAayi\nNMRXb0MA0QA264FuArxt7YymeUGWTkYfJaSWPg5y0lMIJhJ9SYlvJxM9jWZS\n0dqZun4XgNmqpF+L2g7T/Sod90GwKhHUvZbOcb9b+SgR+HD/+6l+sB0QE3bK\ncK3mMCaFwPwI9/M44IMkEmfgFb4N05yx91/Ts+kksaC4JS58xhlyspJj+PjM\nEjlTqgG0nSG74qcGLx3+I67/jHZIvzXhwHF7AqSO0ziWQQV0jEqzaJxW4jzg\neE7kx6tGkx8DrbM/9hoi0ulOwNR3jAEr//GE9vaLe/QZjZEVXLdwg5uiweol\n4NO2Mz+YCNI0XyeWXRq3TnwInzu2q+9LR1XEIaRj6V7enKlBGXX3TAThFFex\nb7Yv\r\n=NRr7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFRGchvR6WmqAJJn8YMyHMPr2Eyx8jczvnjLSkSLywVyAiAQ02Q7UOz/pz5pPAGkcasZKwMQt+PibNYi4pGdM7GczA=="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_1.0.0_1568822721863_0.22513209365079367"},"_hasShrinkwrap":false},"2.0.0-beta.1":{"name":"@prftesp/cle-logger","version":"2.0.0-beta.1","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿CLE Logger\r\n===\r\n\r\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\r\n\r\n## Documentation\r\n\r\n- [Target Environment](#target-environment)\r\n- [Simple Example](#simple-example)\r\n- [Installation](#installation)\r\n- [Features](#features)\r\n- [Configuration](#configuration)\r\n- [API](#api)\r\n\t- [Log sinks](#log-sinks)\r\n\t- [Logging levels](#logging-levels)\r\n\t- [Logging parameters](#logging-parameters)\r\n\t- [Available component names](#available-component-names)\r\n\t- [Available custom property keys](#available-custom-property-keys)\r\n\t- [Log codes](#log-codes)\r\n\t- [Registering Twilio Actions](#registering-twilio-actions)\r\n\t- [Twilio Event Hooks](#twilio-event-hooks)\r\n\t- [Version](#version)\r\n- [Log Object](#log-object)\r\n- [Handling PII Data](#handling-pii-data)\r\n- [Advanced Configuration](#advanced-configuration)\r\n\t- [Remote logging](#remote-logging)\r\n- [Changelog](#changelog)\r\n\r\n## Target Environment\r\n\r\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\r\n\r\n## Simple Example\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"console sink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\n// will log message to console\r\ncleLogger.info(\"log message\");\r\n```\r\n\r\n## Installation\r\n\r\n```bash\r\n$ npm install @prftesp/cle-logger\r\n```\r\n\r\n## Features\r\n\r\n- Runs in Node.js and the browser\r\n- Remote logging\r\n- Multiple log sinks\r\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\r\n- Remote minimum log level overrides\r\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\r\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\r\n- Support for tagging logs with custom component names or correlation tokens\r\n- Send Twilio event hook data to a custom backend\r\n\r\n## Configuration\r\n\r\nTo configure the logger call the ```init``` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\r\n\r\n**Available options**\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// Twilio account sid, required for use with CLE\r\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\r\n\r\n\t// Will be associated with all logs,\r\n\t// if not provided, a unique uuid will be generated\r\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\r\n\t// An array of log sinks that will receive an array of log objects,\r\n\t// and can optionally return a promise\r\n\tlogSinks: [{\r\n\t\t// name of the log sink\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\t// log handler function\r\n\t\tlog: logs => console.log(logs),\r\n\t\t// should this log sink accept PII data, defaults to false\r\n\t\tpiiSafe: true,\r\n\t\t// log level for the sink, defaults to INFO\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n\r\n\t// A way to suppress error messages if set to false, defaults to true\r\n\tlogErrorsToConsole: true,\r\n\r\n\t// If logging Twilio Flex Actions, provide a list of actions \r\n\t// to be excluded from logs\r\n\texcludedFlexActionNames: [\"SendTyping\"],\r\n\r\n\t// A custom endpoint to fetch log level overrides\r\n\t// NOTE: the provided url should always end with 'accountSid=',\r\n\t// the library will append the accountSid\r\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\r\n\r\n\t// An instance of ('@twilio/flex-ui').Manager\r\n\ttwilioFlexManager: manager,\r\n\r\n\t// To send data from Twilio event hooks, to a custom backend,\r\n\t// configure one or more endpoints for each event type\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\r\n\t}\r\n});\r\n```\r\n\r\n## API\r\n\r\nThe logger can be imported as an object in CommonJs or ES6 syntax:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n// or\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n```\r\n\r\nAlternatively, all top level members of the ```cleLogger``` are available as named exports and can be imported separately:\r\n\r\n```js\r\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tlogSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\r\n\r\n// vs\r\n\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tcleLogger.logSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\r\n```\r\n\r\n### Log sinks\r\n\r\nLog sinks can be provided via the ```logSinks``` config. It's an array of objects containing a ```name```, ```piiSafe``` flag to indicate if the sink can handle PII data without concern for privacy violations, ```logLevel```, and a ```log``` method. All provided log sinks will be executed in parallel. You can choose to await the logs to make sure all logging operations are completed.\r\nThe ```log``` method will receive an array of log objects for you to handle. If you're doing async requests in your custom log sinks, it is recommended to return the Promise.\r\n\r\nTwo built-in loggers are available out of the box. ```consoleLogger``` for logging to standard console, and ```remoteLogger``` for sending logs over http(s).\r\n\r\nExample logging to file in Node.js:\r\n\r\n``` js\r\nconst fs = require(\"fs\");\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\ncleLogger.init({\r\n\tcustomLogSinks: [{\r\n\t\tname: \"fileSink\",\r\n\t\tlog: logs => {\r\n\t\t\tfs.appendFile(\r\n\t\t\t\t\"./log.txt\",\r\n\t\t\t\t`${JSON.stringify(logs)}`,\r\n\t\t\t\tconsole.log\r\n\t\t\t);\r\n\t\t}\r\n\t},\r\n\t{\r\n\t\tname: \"remoteSink\",\r\n\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\r\n\t\tlogLevel: cleLogger.logLevels.WARN,\r\n\t\tpiiSafe: true\r\n\t},\r\n\t{\r\n\t\tname: \"consoleSink\",\r\n\t\tlog: cleLogger.consoleSink,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}]\r\n});\r\n```\r\n\r\n### Logging levels\r\n\r\nLogging level constants can be found in ```cleLogger.logLevels```.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.logLevels.VERBOSE; // verbose\r\ncleLogger.logLevels.INFO; // info\r\ncleLogger.logLevels.WARN; // warn\r\ncleLogger.logLevels.ERROR; // error\r\ncleLogger.logLevels.CRITICAL; // critical\r\n```\r\n\r\nThere are functions available for each supported logging level:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(...);\r\ncleLogger.info(...);\r\ncleLogger.warn(...);\r\ncleLogger.error(...);\r\ncleLogger.critical(...);\r\n```\r\n\r\nThe functions are all async, return ```Promise<void>```, and can be optionally awaited. The suggested approach is to not await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is to suggested to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\r\n\r\n### Logging parameters\r\n\r\nAll logging functions take the same parameters:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(\r\n\t// The only required property, no logs will be generated without it\r\n\t\"Log message\",\r\n\r\n\t// Any additional log data you would like to include with the log,\r\n\t// can be any data type\r\n\t{ data: \"test\" },\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\t// CLE uses it to index these properties in Application Insights\r\n\t// For CLE, try to use keys found in 'cleLogger.customPropertyKeys'\r\n\t{ userId: 123 },\r\n\r\n\t// Another useful way to 'tag' your logs for easier searching\r\n\t// Can be any string, but for CLE, try to use available options in 'cleLogger.componentNames'\r\n\t// If you would like more component names added, please let us know \r\n\t\"ProfileComponent\",\r\n\r\n\t// Log code, can be any number used to categorize logs,\r\n\t// there will be a separate document listing CLE codes\r\n\t70123,\r\n\r\n\t// Correlation token to be associated with this log only,\r\n\t// if one is not provided, the token supplied/generated during 'init' will be used\r\n\t\"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\t\r\n\t// Does the log contain PII data, defaults to false. If set to true, the log will be \r\n\t// passed in only to those log sinks that have 'piiSafe' set to true.\r\n\ttrue,\r\n);\r\n```\r\n\r\n### Available component names\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.componentNames.SESSION;\r\ncleLogger.componentNames.DIALER;\r\ncleLogger.componentNames.DASHBOARD;\r\ncleLogger.componentNames.AFTERCALL;\r\ncleLogger.componentNames.SUPERVISOR;\r\n```\r\n\r\n### Available custom property keys\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.customPropertyKeys.CHANNEL_SID;\r\ncleLogger.customPropertyKeys.TASK_SID;\r\ncleLogger.customPropertyKeys.CALL_SID;\r\ncleLogger.customPropertyKeys.WORKFLOW_SID;\r\ncleLogger.customPropertyKeys.WORKER_SID;\r\n```\r\n\r\n### Log codes\r\n\r\nTo categorize a log in one of the following categories, provide a log code in the appropriate range:\r\n\r\n- 100 000 - 109 999 => CALL_STATUS\r\n- 110 000 - 119 999 => IVR\r\n- 120 000 - 129 999 => ROUTING\r\n- 130 000 - 139 999 => AGENT_ACTION\r\n- 140 000 - 149 999 => TRANSFER\r\n- 150 000 - 159 999 => RM_INTEGRATION\r\n- 160 000 - 169 999 => WFM_INTEGRATION\r\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\r\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\r\n\r\n### Registering Twilio Actions\r\n\r\nExample in a Twilio Flex UI plugin:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\n/**\r\n * @param flex { typeof import('@twilio/flex-ui') }\r\n * @param manager { import('@twilio/flex-ui').Manager }\r\n */\r\ninit(flex, manager) {\r\n\tcleLogger.init({\r\n\t\taccountSid: \"your-account-sid\"\r\n\t});\r\n\r\n\tcleLogger.registerTwilioActions(flex);\r\n}\r\n```\r\n\r\n**Note**: A Twilio account sid must be provided during ```init``` with the ```accountSid``` parameter, otherwise the actions won't be logged.\r\n\r\nThe logger will loop through all actions found under ```flex.Actions.actions```, and attach ```after``` event handlers to log the action name and payload. Any private properties found in the payload (starting with '_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Any actions names configured in ```excludedFlexActionNames``` will not be included in the logs.\r\n\r\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\r\n\r\n**Twilio Actions log levels**\r\n\r\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of ```INFO```.\r\n\r\n``` js\r\nAcceptTask: INFO\r\nCancelTransfer: INFO\r\nCompleteTask: INFO\r\nHangupCall: INFO\r\nHideDirectory: VERBOSE\r\nHistoryGo: VERBOSE\r\nHistoryGoBack: VERBOSE\r\nHistoryGoForward: VERBOSE\r\nHistoryPush: VERBOSE\r\nHistoryReplace: VERBOSE\r\nHoldCall: INFO\r\nHoldParticipant: INFO\r\nKickParticipant: INFO\r\nLogout: INFO\r\nMonitorCall: INFO\r\nNavigateToView: INFO\r\nRejectTask: INFO\r\nSelectTask: INFO\r\nSelectTaskInSupervisor: INFO\r\nSelectWorkerInSupervisor: INFO\r\nSendMessage: INFO\r\nSendTyping: VERBOSE\r\nSetActivity: INFO\r\nSetInputText: VERBOSE\r\nShowDirectory: VERBOSE\r\nStopMonitoringCall: INFO\r\nToggleMute: INFO\r\nToggleSidebar: VERBOSE\r\nTransferTask: INFO\r\nUnholdCall: INFO\r\nUnholdParticipant: INFO\r\nWrapupTask: INFO\r\n```\r\n\r\n### Twilio Event Hooks\r\n\r\nCurrently, Twilio only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with ```twilioEventHooks``` option, and use the following functions to send event data to a custom endpoint.\r\n\r\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The data format should be the same as received from Twilio. The content type of data sent by the logger is ```application/x-www-form-urlencoded```.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// unrelated properties omitted\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\r\n\t}\r\n});\r\n\r\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\r\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\r\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\r\n```\r\n\r\nExample Azure Function hook for debugger events:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\nmodule.exports = async function (context, req) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\t}\r\n\t});\r\n\r\n\t// log to console\r\n\tcleLogger.info(\"Received Twilio debugger event\");\r\n\t\r\n\t// send event data to a configured url\r\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\r\n};\r\n```\r\n\r\n### Version\r\n\r\nVersion will be automatically included in all logs.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.version; // 1.0.0\r\n```\r\n\r\n## Log Object\r\n\r\n```js\r\n{\r\n\t// Account sid if provided during init\r\n\taccountSid: string;\r\n\r\n\t// Any additional log data to be included with the log\r\n\tadditionalData: unknown;\r\n\t\r\n\t// Component name, if provided\r\n\tcomponentName: string;\r\n\r\n\t// Generated or configured token\r\n\tcorrelationToken: string;\r\n\r\n\t// Current UTC datetime in ISO 8601 format\r\n\teventDate: string;\r\n\r\n\t// Log level\r\n\tlevel: string;\r\n\r\n\t// Log code, if provided\r\n\tlogCode: number;\r\n\r\n\t// Log message\r\n\tmessage: string;\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\tproperties: object;\r\n\r\n\t// BrowserFunction if the environment is the browser\r\n\t// CloudFunction if the environment is Node.js\r\n\tsource: string;\r\n\r\n\t// Logging library version \r\n\tloggerVersion: string;\r\n}\r\n```\r\n\r\n## Handling PII Data\r\n\r\nThe logger provides support for marking logs as containing PII (personally identifiable data), and configuring each log sink to indicate if it can handle PII data. By default, log sinks are not marked as PII safe, meaning they will not receive logs marked as containing PII. Marking a log sink as PII safe, means that the logger should be able to handle PII data without privacy violations (for example, a remote log sink that sends data to an API that masks PII data would be considered safe).\r\n\r\nAdditionally, the logger exports a function ```tagPII``` which can be used to tag parts of a string message as PII for easier masking. Tags can be used by backend systems to mask the data with a simple regex search and replace.\r\n\r\nExample tagged output:\r\n\r\n```js\r\nimport { tagPII } from \"@prftesp/cle-logger\";\r\n\r\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\r\n```\r\n\r\n## Advanced Configuration\r\n\r\n### Remote logging\r\n\r\nThe library provides a way to provide you own remote log url, or to remotely override the min log level. This can be useful for temporarily setting the log level to verbose in a production environment, for quick troubleshooting.\r\n\r\n**Remote log endpoint**\r\n\r\nTo implement your own remote logging endpoint, all you have to do is accept ```POST``` requests, with a JSON payload of an array of [log objects](#log-object), and return either ```{ success: true }``` or ```{ success: false }```. The endpoint should always return a ```200 OK``` status code. A good use-case would be leveraging a serverless api platform to output logs to a queue.\r\n\r\nExample Azure Function:\r\n\r\n```js\r\nmodule.exports = async function (context, req) {\r\n    context.log(req.body); // body will have the log array\r\n\r\n    context.res = {\r\n        status: 200,\r\n        body: { success: true }\r\n    };\r\n};\r\n```\r\n\r\n**Remote log level override**\r\n\r\nThe ```minLogLevelUrl``` configuration property is used to provide an endpoint that accepts ```GET``` requests and a ```accountSid``` query string parameter. ```accountSid``` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the ```minLogLevel``` configuration property. If null is returned, the ```minLogLevel``` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a ```200 OK``` status code and use the ```success``` property to indicate failure.\r\n\r\nExample success response:\r\n\r\n```json\r\n{ \r\n\t\"success\": true,\r\n\t\"minLogLevel\": \"warn\"\r\n}\r\n```\r\n\r\nExample error response:\r\n\r\n```json\r\n{ \r\n\t\"success\": false,\r\n\t\"minLogLevel\": null\r\n}\r\n```\r\n\r\n## Changelog\r\n\r\n### v2.0.0 [2019-12-16]\r\n\r\n*New features*\r\n\r\n\r\n*Breaking changes*\r\n\r\n\r\n*Migration guide*\r\n\r\n\r\n### v1.0.0 [2019-09-12]\r\n\r\n- Initial release\r\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.0.0-beta.1","_nodeVersion":"10.15.2","_npmVersion":"6.11.3","dist":{"integrity":"sha512-XmFv2CgIUPVEWkkB2WDu9V20mf5BABzt512+vnp6eJoUORyZ4oSNgkuNZvAJmjLWvPNSuuYUgJ6Z66oI7+MFcA==","shasum":"bbe5af10fd69f0a3121ee2d0ce93c3b5617dbe8d","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.0.0-beta.1.tgz","fileCount":17,"unpackedSize":58701,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd/N1fCRA9TVsSAnZWagAAP64P/j1xrxCcZOGl3XzeBgHM\nn1ONQGDRCebG3gHUlhxZLH42GBz37aFCwxNSpjosJMm+ACAfM2I8XKAQ7Ngz\ngwgwuhZpJqtFxLYpe5CwR0LuF69gBXxzbMAHtKAPMr9GCnff2BMehnUy9PEw\nPuKvcEWhCH2MbJ433aAuUBYLWl+pBXAJrDOE0lRadp1lOXlCBmfwHiDltLzl\nRA/w0coTEHac7Yv+9kzjzicfJMoPhJKPoC9kVQGNw/hQOW46qYy11EtIKGEK\nLpzmWQIsVbaqfEBDGkC37mtTTJEMG3NfnF4vcwH83Y+pvyr1D55nheELzB5q\nM0JyeSYihrzSBKeplWoVnhcQYaj23F1Qo9u1fRH+680/I3jN7oXKBUluRbF0\nhzPWCV5ngPyCgXb6XaD9ZvNeee88iBtQaSrfBxK/W/g/dlOpLB48+RbASdrW\nAF/qdSKOIpi1wRueUZe9zQuSdy3w+W5yHfOV+Uu5wSvUnKlQY8lh8F8CeAHa\n1WyBCK6keE6tEElmV0rBhe+JHLoLiT3wBgowHUGHFr2MCZbtP/9nRfWfL8aT\ndVTfkCGl0PgJGNAArggCgaAvx12yqxGXCVc928IaCCCS02MYlSUqoV1k4kvX\n130ZirWeASz7C0tVZPqyC0mBusbsoXtvC2R8zI4NPR1rx9e6S+FmcW7iBTCO\nhdeF\r\n=tnbS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIB8oFYTl1PEbl6gdgimP4qi6nidYqPQDuyNFdF6DSaj0AiBl+Up1hG4rKQpm/KnbKr4fP19hoDQcv2oBkymAiVx3Gg=="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.0.0-beta.1_1576852830684_0.6730197845137151"},"_hasShrinkwrap":false},"2.0.0-beta.2":{"name":"@prftesp/cle-logger","version":"2.0.0-beta.2","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿CLE Logger\r\n===\r\n\r\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\r\n\r\n## Documentation\r\n\r\n- [Target Environment](#target-environment)\r\n- [Simple Example](#simple-example)\r\n- [Installation](#installation)\r\n- [Features](#features)\r\n- [Configuration](#configuration)\r\n- [API](#api)\r\n\t- [Log sinks](#log-sinks)\r\n\t- [Logging levels](#logging-levels)\r\n\t- [Logging parameters](#logging-parameters)\r\n\t- [Available component names](#available-component-names)\r\n\t- [Available custom property keys](#available-custom-property-keys)\r\n\t- [Log codes](#log-codes)\r\n\t- [Registering Twilio Actions](#registering-twilio-actions)\r\n\t- [Twilio Event Hooks](#twilio-event-hooks)\r\n\t- [Version](#version)\r\n- [Log Object](#log-object)\r\n- [Handling PII Data](#handling-pii-data)\r\n- [Advanced Configuration](#advanced-configuration)\r\n\t- [Remote logging](#remote-logging)\r\n- [Changelog](#changelog)\r\n\r\n## Target Environment\r\n\r\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\r\n\r\n## Simple Example\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"console sink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\n// will log message to console\r\ncleLogger.info(\"log message\");\r\n```\r\n\r\n## Installation\r\n\r\n```bash\r\n$ npm install @prftesp/cle-logger\r\n```\r\n\r\n## Features\r\n\r\n- Runs in Node.js and the browser\r\n- Remote logging\r\n- Multiple log sinks\r\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\r\n- Remote minimum log level overrides\r\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\r\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\r\n- Support for tagging logs with custom component names or correlation tokens\r\n- Send Twilio event hook data to a custom backend\r\n\r\n## Configuration\r\n\r\nTo configure the logger call the ```init``` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\r\n\r\n**Available options**\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// Twilio account sid, required for use with CLE\r\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\r\n\r\n\t// Will be associated with all logs,\r\n\t// if not provided, a unique uuid will be generated\r\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\r\n\t// An array of log sinks that will receive an array of log objects,\r\n\t// and can optionally return a promise\r\n\tlogSinks: [{\r\n\t\t// name of the log sink\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\t// log handler function\r\n\t\tlog: logs => console.log(logs),\r\n\t\t// should this log sink accept PII data, defaults to false\r\n\t\tpiiSafe: true,\r\n\t\t// log level for the sink, defaults to INFO\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n\r\n\t// A way to suppress error messages if set to false, defaults to true\r\n\tlogErrorsToConsole: true,\r\n\r\n\t// If logging Twilio Flex Actions, provide a list of actions \r\n\t// to be excluded from logs\r\n\texcludedFlexActionNames: [\"SendTyping\"],\r\n\r\n\t// A custom endpoint to fetch log level overrides\r\n\t// NOTE: the provided url should always end with 'accountSid=',\r\n\t// the library will append the accountSid\r\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\r\n\r\n\t// An instance of ('@twilio/flex-ui').Manager\r\n\ttwilioFlexManager: manager,\r\n\r\n\t// To send data from Twilio event hooks, to a custom backend,\r\n\t// configure one or more endpoints for each event type\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\r\n\t}\r\n});\r\n```\r\n\r\n## API\r\n\r\nThe logger can be imported as an object in CommonJs or ES6 syntax:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n// or\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n```\r\n\r\nAlternatively, all top level members of the ```cleLogger``` are available as named exports and can be imported separately:\r\n\r\n```js\r\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tlogSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\r\n\r\n// vs\r\n\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tcleLogger.logSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\r\n```\r\n\r\n### Log sinks\r\n\r\nLog sinks can be provided via the ```logSinks``` config. It's an array of objects containing a ```name```, ```piiSafe``` flag to indicate if the sink can handle PII data without concern for privacy violations, ```logLevel```, and a ```log``` method. All provided log sinks will be executed in parallel. You can choose to await the logs to make sure all logging operations are completed.\r\nThe ```log``` method will receive an array of log objects for you to handle. If you're doing async requests in your custom log sinks, it is recommended to return the Promise.\r\n\r\nTwo built-in loggers are available out of the box. ```consoleLogger``` for logging to standard console, and ```remoteLogger``` for sending logs over http(s).\r\n\r\nLog sink object:\r\n\r\n``` js\r\n{\r\n\t// name of the log sink\r\n\tname: \"exampleConsoleSink\",\r\n\t// log handler function\r\n\tlog: logs => console.log(logs),\r\n\t// should this log sink accept PII data, defaults to false\r\n\tpiiSafe: true,\r\n\t// log level for the sink, defaults to INFO\r\n\tlogLevel: cleLogger.logLevels.VERBOSE\r\n}\r\n```\r\n\r\nExample log sinks:\r\n\r\n``` js\r\nconst fs = require(\"fs\");\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"fileSink\",\r\n\t\tlog: logs => {\r\n\t\t\tfs.appendFile(\r\n\t\t\t\t\"./log.txt\",\r\n\t\t\t\t`${JSON.stringify(logs)}`,\r\n\t\t\t\tconsole.log\r\n\t\t\t);\r\n\t\t}\r\n\t},\r\n\t{\r\n\t\tname: \"remoteSink\",\r\n\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\r\n\t\tlogLevel: cleLogger.logLevels.WARN,\r\n\t\tpiiSafe: true\r\n\t},\r\n\t{\r\n\t\tname: \"consoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}]\r\n});\r\n```\r\n\r\n### Logging levels\r\n\r\nLogging level constants can be found in ```cleLogger.logLevels```.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.logLevels.VERBOSE; // verbose\r\ncleLogger.logLevels.INFO; // info\r\ncleLogger.logLevels.WARN; // warn\r\ncleLogger.logLevels.ERROR; // error\r\ncleLogger.logLevels.CRITICAL; // critical\r\n```\r\n\r\nThere are functions available for each supported logging level:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(...);\r\ncleLogger.info(...);\r\ncleLogger.warn(...);\r\ncleLogger.error(...);\r\ncleLogger.critical(...);\r\n```\r\n\r\nThe functions are all async, return ```Promise<void>```, and can be optionally awaited. The suggested approach is to not await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is to suggested to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\r\n\r\n### Logging parameters\r\n\r\nAll logging functions take the same parameters:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(\r\n\t// The only required property, no logs will be generated without it\r\n\t\"Log message\",\r\n\r\n\t// Any additional log data you would like to include with the log,\r\n\t// can be any data type\r\n\t{ data: \"test\" },\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\t// CLE uses it to index these properties in Application Insights\r\n\t// For CLE, try to use keys found in 'cleLogger.customPropertyKeys'\r\n\t{ userId: 123 },\r\n\r\n\t// Another useful way to 'tag' your logs for easier searching\r\n\t// Can be any string, but for CLE, try to use available options in 'cleLogger.componentNames'\r\n\t// If you would like more component names added, please let us know \r\n\t\"ProfileComponent\",\r\n\r\n\t// Log code, can be any number used to categorize logs,\r\n\t// there will be a separate document listing CLE codes\r\n\t70123,\r\n\r\n\t// Correlation token to be associated with this log only,\r\n\t// if one is not provided, the token supplied/generated during 'init' will be used\r\n\t\"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\t\r\n\t// Does the log contain PII data, defaults to false. If set to true, the log will be \r\n\t// passed in only to those log sinks that have 'piiSafe' set to true.\r\n\ttrue,\r\n);\r\n```\r\n\r\n### Available component names\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.componentNames.SESSION;\r\ncleLogger.componentNames.DIALER;\r\ncleLogger.componentNames.DASHBOARD;\r\ncleLogger.componentNames.AFTERCALL;\r\ncleLogger.componentNames.SUPERVISOR;\r\n```\r\n\r\n### Available custom property keys\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.customPropertyKeys.CHANNEL_SID;\r\ncleLogger.customPropertyKeys.TASK_SID;\r\ncleLogger.customPropertyKeys.CALL_SID;\r\ncleLogger.customPropertyKeys.WORKFLOW_SID;\r\ncleLogger.customPropertyKeys.WORKER_SID;\r\n```\r\n\r\n### Log codes\r\n\r\nTo categorize a log in one of the following categories, provide a log code in the appropriate range:\r\n\r\n- 100 000 - 109 999 => CALL_STATUS\r\n- 110 000 - 119 999 => IVR\r\n- 120 000 - 129 999 => ROUTING\r\n- 130 000 - 139 999 => AGENT_ACTION\r\n- 140 000 - 149 999 => TRANSFER\r\n- 150 000 - 159 999 => RM_INTEGRATION\r\n- 160 000 - 169 999 => WFM_INTEGRATION\r\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\r\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\r\n\r\n### Registering Twilio Actions\r\n\r\nExample in a Twilio Flex UI plugin:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\n/**\r\n * @param flex { typeof import('@twilio/flex-ui') }\r\n * @param manager { import('@twilio/flex-ui').Manager }\r\n */\r\ninit(flex, manager) {\r\n\tcleLogger.init({\r\n\t\taccountSid: \"your-account-sid\"\r\n\t});\r\n\r\n\tcleLogger.registerTwilioActions(flex);\r\n}\r\n```\r\n\r\n**Note**: A Twilio account sid must be provided during ```init``` with the ```accountSid``` parameter, otherwise the actions won't be logged.\r\n\r\nThe logger will loop through all actions found under ```flex.Actions.actions```, and attach ```after``` event handlers to log the action name and payload. Any private properties found in the payload (starting with '_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Any actions names configured in ```excludedFlexActionNames``` will not be included in the logs.\r\n\r\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\r\n\r\n**Twilio Actions log levels**\r\n\r\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of ```INFO```.\r\n\r\n``` js\r\nAcceptTask: INFO\r\nCancelTransfer: INFO\r\nCompleteTask: INFO\r\nHangupCall: INFO\r\nHideDirectory: VERBOSE\r\nHistoryGo: VERBOSE\r\nHistoryGoBack: VERBOSE\r\nHistoryGoForward: VERBOSE\r\nHistoryPush: VERBOSE\r\nHistoryReplace: VERBOSE\r\nHoldCall: INFO\r\nHoldParticipant: INFO\r\nKickParticipant: INFO\r\nLogout: INFO\r\nMonitorCall: INFO\r\nNavigateToView: INFO\r\nRejectTask: INFO\r\nSelectTask: INFO\r\nSelectTaskInSupervisor: INFO\r\nSelectWorkerInSupervisor: INFO\r\nSendMessage: INFO\r\nSendTyping: VERBOSE\r\nSetActivity: INFO\r\nSetInputText: VERBOSE\r\nShowDirectory: VERBOSE\r\nStopMonitoringCall: INFO\r\nToggleMute: INFO\r\nToggleSidebar: VERBOSE\r\nTransferTask: INFO\r\nUnholdCall: INFO\r\nUnholdParticipant: INFO\r\nWrapupTask: INFO\r\n```\r\n\r\n### Twilio Event Hooks\r\n\r\nCurrently, Twilio only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with ```twilioEventHooks``` option, and use the following functions to send event data to a custom endpoint.\r\n\r\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The data format should be the same as received from Twilio. The content type of data sent by the logger is ```application/x-www-form-urlencoded```.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// unrelated properties omitted\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\r\n\t}\r\n});\r\n\r\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\r\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\r\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\r\n```\r\n\r\nExample Azure Function hook for debugger events:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\nmodule.exports = async function (context, req) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\t}\r\n\t});\r\n\r\n\t// log to console\r\n\tcleLogger.info(\"Received Twilio debugger event\");\r\n\t\r\n\t// send event data to a configured url\r\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\r\n};\r\n```\r\n\r\n### Version\r\n\r\nVersion will be automatically included in all logs.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.version; // 1.0.0\r\n```\r\n\r\n## Log Object\r\n\r\n```js\r\n{\r\n\t// Account sid if provided during init\r\n\taccountSid: string;\r\n\r\n\t// Any additional log data to be included with the log\r\n\tadditionalData: unknown;\r\n\t\r\n\t// Component name, if provided\r\n\tcomponentName: string;\r\n\r\n\t// Generated or configured token\r\n\tcorrelationToken: string;\r\n\r\n\t// Current UTC datetime in ISO 8601 format\r\n\teventDate: string;\r\n\r\n\t// Log level\r\n\tlevel: string;\r\n\r\n\t// Log code, if provided\r\n\tlogCode: number;\r\n\r\n\t// Log message\r\n\tmessage: string;\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\tproperties: object;\r\n\r\n\t// BrowserFunction if the environment is the browser\r\n\t// CloudFunction if the environment is Node.js\r\n\tsource: string;\r\n\r\n\t// Logging library version \r\n\tloggerVersion: string;\r\n}\r\n```\r\n\r\n## Handling PII Data\r\n\r\nThe logger provides support for marking logs as containing PII (personally identifiable data), and configuring each log sink to indicate if it can handle PII data. By default, log sinks are not marked as PII safe, meaning they will not receive logs marked as containing PII. Marking a log sink as PII safe, means that the logger should be able to handle PII data without privacy violations (for example, a remote log sink that sends data to an API that masks PII data would be considered safe).\r\n\r\nAdditionally, the logger exports a function ```tagPII``` which can be used to tag parts of a string message as PII for easier masking. Tags can be used by backend systems to mask the data with a simple regex search and replace.\r\n\r\nExample tagged output:\r\n\r\n```js\r\nimport { tagPII } from \"@prftesp/cle-logger\";\r\n\r\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\r\n```\r\n\r\n## Advanced Configuration\r\n\r\n### Remote logging\r\n\r\nThe library provides a way to provide you own remote log url, or to remotely override the min log level. This can be useful for temporarily setting the log level to verbose in a production environment, for quick troubleshooting.\r\n\r\n**Remote log endpoint**\r\n\r\nTo implement your own remote logging endpoint, all you have to do is accept ```POST``` requests, with a JSON payload of an array of [log objects](#log-object), and return either ```{ success: true }``` or ```{ success: false }```. The endpoint should always return a ```200 OK``` status code. A good use-case would be leveraging a serverless api platform to output logs to a queue.\r\n\r\nExample Azure Function:\r\n\r\n```js\r\nmodule.exports = async function (context, req) {\r\n    context.log(req.body); // body will have the log array\r\n\r\n    context.res = {\r\n        status: 200,\r\n        body: { success: true }\r\n    };\r\n};\r\n```\r\n\r\n**Remote log level override**\r\n\r\nThe ```minLogLevelUrl``` configuration property is used to provide an endpoint that accepts ```GET``` requests and a ```accountSid``` query string parameter. ```accountSid``` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the ```minLogLevel``` configuration property. If null is returned, the ```minLogLevel``` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a ```200 OK``` status code and use the ```success``` property to indicate failure.\r\n\r\nExample success response:\r\n\r\n```json\r\n{ \r\n\t\"success\": true,\r\n\t\"minLogLevel\": \"warn\"\r\n}\r\n```\r\n\r\nExample error response:\r\n\r\n```json\r\n{ \r\n\t\"success\": false,\r\n\t\"minLogLevel\": null\r\n}\r\n```\r\n\r\n## Changelog\r\n\r\n### v2.0.0 [2019-12-16]\r\n\r\n**New features**\r\n- All logging output is now configured using the ```logSinks``` property. The library provides two commonly used loggers - ```consoleLogger``` and a ```remoteLogger``` that accepts the log url as a parameter. [Configuration](#configuration)\r\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\r\n- Log functions have a new optional ```containsPII``` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\r\n- A new helper function ```tagPII``` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\r\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\r\n\r\n**Breaking changes**\r\n- ```customLogSink``` configuration renamed to ```logSinks```\r\n- Removed ```disableRemoteLogging``` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\r\n- Removed ```logToConsole``` configuration. This is now configured as a log sink with a ```consoleLogger``` helper function.\r\n- Removed ```logUrl``` configuration. This is now configured as a log sink with a ```remoteLogger``` helper function.\r\n- Removed ```minLogLevel``` configuration. This is now configured per log sink.\r\n\r\n**Migration guide**\r\n``` diff\r\nimport cleLogger from \"@prftesp/cle-logger\";\r\n\r\n cleLogger.init({\r\n-\tdisableRemoteLogSinks: true, // no direct replacement\r\n-   logToConsole: true, // replaced by the \"console logger\" sink\r\n-   logUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\r\n-   minLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\r\n-   customLogSinks: [{\r\n-    \tname: \"custom sink\",\r\n-     log: logs => {/* your custom logging */}\r\n-   }]\r\n+   logSinks: [{\r\n+    \tname: \"custom sink\",\r\n+   \tlog: logs => {/* your custom logging */},\r\n+   \tlogLevel: cleLogger.logLevels.INFO,\r\n+   \tpiiSafe: true\r\n+  \t},\r\n+  \t{\r\n+    \tname: \"console logger\",\r\n+    \tlog: consoleLogger,\r\n+    \tlogLevel: cleLogger.logLevels.INFO,\r\n+    \tpiiSafe: true,\r\n+  \t},\r\n+  \t{\r\n+    \tname: \"remote logger\",\r\n+    \tlog: remoteLogger(\"http://log.output/api\"),\r\n+    \tlogLevel: cleLogger.logLevels.INFO,\r\n+    \tpiiSafe: true,\r\n+  \t}]\r\n });\r\n```\r\n\r\n### v1.0.0 [2019-09-12]\r\n\r\n- Initial release\r\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.0.0-beta.2","_nodeVersion":"10.15.2","_npmVersion":"6.11.3","dist":{"integrity":"sha512-NxM4BR5t5m9HNGKx0N/mrfY3ZxEb+6atXQ62w06x8jbBQ9wMZsn+FoNDeXlA4L/RDe2mEcol05+5F/E0BoWL1w==","shasum":"5e029bd04da58de72397811266ddeee87fd3e0f2","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.0.0-beta.2.tgz","fileCount":17,"unpackedSize":61386,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd/PrRCRA9TVsSAnZWagAA/b4P/joyZvuVqavyabFZzsev\nF/QqcfZqW/HQsknudh2N/dXMWoRpjOHyJQkG5R4fMV9D4cA/OHDiCD/030fA\n1nxhNm0wSb6uK82MP2lfKqarjVemd5TT4iR6LZNnT/7w2SDYP5seHvySGdaj\nVGS4Z95ql5gmrhfDbhwujUN1fJ3x0JEDFG9MbbdwWsJ0nsdev9JQiyyYfBng\n1dIGUxPeqvsnMGSPjTkuGQrmqZ3ESQV1g7L0F7qH/RK8hhTfwykdqSlljIFj\nWmtWxUZYJkjE2inTDrON0T8sGqNWwuFhCVKPz9NUYQ1fWnP9O6IO4HIr4olo\n3LNyauXbCnxhgAhcSoMEG1NmUCAKo04Xh47VUXPV6aa2M8Ry87L07g86urJ3\nZk0Czxt6f7ecUrafHs4aq556ekShmOanGH7uBqm4lgzm9Q+wmTgH8amsNSNN\nJN7lPlvJrKmvAubZ0FWAcmW0qiznZDd1fJnmckje9IYyZKlWKKTRlPwG/s5H\nN0MEtmxJqkV4FRBJioywssvF7YNOePUOJrszYyexCL7zBgA6D4/sZszBe32J\nKqUsPT8lvFhqGI961f1PPM+a8V4wv4h6Rs5JdED1uWcUyMoZXJiKaqyaPm1B\n5AplbWYCNchtGJpJlg3b6yF1am8NOQBtnffmAyXBnkvXEe7+ReFLVootWqTu\n4KcI\r\n=IkHI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEernMfgZtJMgGPoXx3SrLU5j5YjR/jrLFX1hjKTb0QXAiEA2OmQ5kG9pCayktTzf7Ygkio14eQxdgU1e2CwP0Dbwus="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.0.0-beta.2_1576860368629_0.8511948775383695"},"_hasShrinkwrap":false},"2.0.0-beta.3":{"name":"@prftesp/cle-logger","version":"2.0.0-beta.3","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿CLE Logger\r\n===\r\n\r\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\r\n\r\n## Documentation\r\n\r\n- [Target Environment](#target-environment)\r\n- [Simple Example](#simple-example)\r\n- [Installation](#installation)\r\n- [Features](#features)\r\n- [Configuration](#configuration)\r\n- [API](#api)\r\n\t- [Log sinks](#log-sinks)\r\n\t- [Logging levels](#logging-levels)\r\n\t- [Logging parameters](#logging-parameters)\r\n\t- [Available component names](#available-component-names)\r\n\t- [Available custom property keys](#available-custom-property-keys)\r\n\t- [Log codes](#log-codes)\r\n\t- [Registering Twilio Actions](#registering-twilio-actions)\r\n\t- [Twilio Event Hooks](#twilio-event-hooks)\r\n\t- [Version](#version)\r\n- [Log Object](#log-object)\r\n- [Handling PII Data](#handling-pii-data)\r\n- [Advanced Configuration](#advanced-configuration)\r\n\t- [Remote logging](#remote-logging)\r\n- [Changelog](#changelog)\r\n\r\n## Target Environment\r\n\r\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\r\n\r\n## Simple Example\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"console sink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\n// will log message to console\r\ncleLogger.info(\"log message\");\r\n```\r\n\r\n## Installation\r\n\r\n```bash\r\n$ npm install @prftesp/cle-logger\r\n```\r\n\r\n## Features\r\n\r\n- Runs in Node.js and the browser\r\n- Remote logging\r\n- Multiple log sinks\r\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\r\n- Remote minimum log level overrides\r\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\r\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\r\n- Support for tagging logs with custom component names or correlation tokens\r\n- Send Twilio event hook data to a custom backend\r\n\r\n## Configuration\r\n\r\nTo configure the logger call the ```init``` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\r\n\r\n**Available options**\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// Twilio account sid, required for use with CLE\r\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\r\n\r\n\t// Will be associated with all logs,\r\n\t// if not provided, a unique uuid will be generated\r\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\r\n\t// An array of log sinks that will receive an array of log objects,\r\n\t// and can optionally return a promise\r\n\tlogSinks: [{\r\n\t\t// name of the log sink\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\t// log handler function\r\n\t\tlog: logs => console.log(logs),\r\n\t\t// should this log sink accept PII data, defaults to false\r\n\t\tpiiSafe: true,\r\n\t\t// log level for the sink, defaults to INFO\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n\r\n\t// A way to suppress error messages if set to false, defaults to true\r\n\tlogErrorsToConsole: true,\r\n\r\n\t// If logging Twilio Flex Actions, provide a list of actions \r\n\t// to be excluded from logs\r\n\texcludedFlexActionNames: [\"SendTyping\"],\r\n\r\n\t// A custom endpoint to fetch log level overrides\r\n\t// NOTE: the provided url should always end with 'accountSid=',\r\n\t// the library will append the accountSid\r\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\r\n\r\n\t// An instance of ('@twilio/flex-ui').Manager\r\n\ttwilioFlexManager: manager,\r\n\r\n\t// To send data from Twilio event hooks, to a custom backend,\r\n\t// configure one or more endpoints for each event type\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\r\n\t}\r\n});\r\n```\r\n\r\n## API\r\n\r\nThe logger can be imported as an object in CommonJs or ES6 syntax:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n// or\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n```\r\n\r\nAlternatively, all top level members of the ```cleLogger``` are available as named exports and can be imported separately:\r\n\r\n```js\r\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tlogSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\r\n\r\n// vs\r\n\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tcleLogger.logSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\r\n```\r\n\r\n### Log sinks\r\n\r\nLog sinks can be provided via the ```logSinks``` config. It's an array of objects containing a ```name```, ```piiSafe``` flag to indicate if the sink can handle PII data without concern for privacy violations, ```logLevel```, and a ```log``` method. All provided log sinks will be executed in parallel. You can choose to await the logs to make sure all logging operations are completed.\r\nThe ```log``` method will receive an array of log objects for you to handle. If you're doing async requests in your custom log sinks, it is recommended to return the Promise.\r\n\r\nTwo built-in loggers are available out of the box. ```consoleLogger``` for logging to standard console, and ```remoteLogger``` for sending logs over http(s).\r\n\r\nLog sink object:\r\n\r\n``` js\r\n{\r\n\t// name of the log sink\r\n\tname: \"exampleConsoleSink\",\r\n\t// log handler function\r\n\tlog: logs => console.log(logs),\r\n\t// should this log sink accept PII data, defaults to false\r\n\tpiiSafe: true,\r\n\t// log level for the sink, defaults to INFO\r\n\tlogLevel: cleLogger.logLevels.VERBOSE\r\n}\r\n```\r\n\r\nExample log sinks:\r\n\r\n``` js\r\nconst fs = require(\"fs\");\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"fileSink\",\r\n\t\tlog: logs => {\r\n\t\t\tfs.appendFile(\r\n\t\t\t\t\"./log.txt\",\r\n\t\t\t\t`${JSON.stringify(logs)}`,\r\n\t\t\t\tconsole.log\r\n\t\t\t);\r\n\t\t}\r\n\t},\r\n\t{\r\n\t\tname: \"remoteSink\",\r\n\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\r\n\t\tlogLevel: cleLogger.logLevels.WARN,\r\n\t\tpiiSafe: true\r\n\t},\r\n\t{\r\n\t\tname: \"consoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}]\r\n});\r\n```\r\n\r\n### Logging levels\r\n\r\nLogging level constants can be found in ```cleLogger.logLevels```.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.logLevels.VERBOSE; // verbose\r\ncleLogger.logLevels.INFO; // info\r\ncleLogger.logLevels.WARN; // warn\r\ncleLogger.logLevels.ERROR; // error\r\ncleLogger.logLevels.CRITICAL; // critical\r\n```\r\n\r\nThere are functions available for each supported logging level:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(...);\r\ncleLogger.info(...);\r\ncleLogger.warn(...);\r\ncleLogger.error(...);\r\ncleLogger.critical(...);\r\n```\r\n\r\nThe functions are all async, return ```Promise<void>```, and can be optionally awaited. The suggested approach is to not await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is to suggested to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\r\n\r\n### Logging parameters\r\n\r\nAll logging functions take the same parameters:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(\r\n\t// The only required property, no logs will be generated without it\r\n\t\"Log message\",\r\n\r\n\t// Any additional log data you would like to include with the log,\r\n\t// can be any data type\r\n\t{ data: \"test\" },\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\t// CLE uses it to index these properties in Application Insights\r\n\t// For CLE, try to use keys found in 'cleLogger.customPropertyKeys'\r\n\t{ userId: 123 },\r\n\r\n\t// Another useful way to 'tag' your logs for easier searching\r\n\t// Can be any string, but for CLE, try to use available options in 'cleLogger.componentNames'\r\n\t// If you would like more component names added, please let us know \r\n\t\"ProfileComponent\",\r\n\r\n\t// Log code, can be any number used to categorize logs,\r\n\t// there will be a separate document listing CLE codes\r\n\t70123,\r\n\r\n\t// Correlation token to be associated with this log only,\r\n\t// if one is not provided, the token supplied/generated during 'init' will be used\r\n\t\"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\t\r\n\t// Does the log contain PII data, defaults to false. If set to true, the log will be \r\n\t// passed in only to those log sinks that have 'piiSafe' set to true.\r\n\ttrue,\r\n);\r\n```\r\n\r\n### Available component names\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.componentNames.SESSION;\r\ncleLogger.componentNames.DIALER;\r\ncleLogger.componentNames.DASHBOARD;\r\ncleLogger.componentNames.AFTERCALL;\r\ncleLogger.componentNames.SUPERVISOR;\r\n```\r\n\r\n### Available custom property keys\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.customPropertyKeys.CHANNEL_SID;\r\ncleLogger.customPropertyKeys.TASK_SID;\r\ncleLogger.customPropertyKeys.CALL_SID;\r\ncleLogger.customPropertyKeys.WORKFLOW_SID;\r\ncleLogger.customPropertyKeys.WORKER_SID;\r\n```\r\n\r\n### Log codes\r\n\r\nTo categorize a log in one of the following categories, provide a log code in the appropriate range:\r\n\r\n- 100 000 - 109 999 => CALL_STATUS\r\n- 110 000 - 119 999 => IVR\r\n- 120 000 - 129 999 => ROUTING\r\n- 130 000 - 139 999 => AGENT_ACTION\r\n- 140 000 - 149 999 => TRANSFER\r\n- 150 000 - 159 999 => RM_INTEGRATION\r\n- 160 000 - 169 999 => WFM_INTEGRATION\r\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\r\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\r\n\r\n### Registering Twilio Actions\r\n\r\nExample in a Twilio Flex UI plugin:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\n/**\r\n * @param flex { typeof import('@twilio/flex-ui') }\r\n * @param manager { import('@twilio/flex-ui').Manager }\r\n */\r\ninit(flex, manager) {\r\n\tcleLogger.init({\r\n\t\taccountSid: \"your-account-sid\"\r\n\t});\r\n\r\n\tcleLogger.registerTwilioActions(flex);\r\n}\r\n```\r\n\r\n**Note**: A Twilio account sid must be provided during ```init``` with the ```accountSid``` parameter, otherwise the actions won't be logged.\r\n\r\nThe logger will loop through all actions found under ```flex.Actions.actions```, and attach ```after``` event handlers to log the action name and payload. Any private properties found in the payload (starting with '_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Any actions names configured in ```excludedFlexActionNames``` will not be included in the logs.\r\n\r\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\r\n\r\n**Twilio Actions log levels**\r\n\r\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of ```INFO```.\r\n\r\n``` js\r\nAcceptTask: INFO\r\nCancelTransfer: INFO\r\nCompleteTask: INFO\r\nHangupCall: INFO\r\nHideDirectory: VERBOSE\r\nHistoryGo: VERBOSE\r\nHistoryGoBack: VERBOSE\r\nHistoryGoForward: VERBOSE\r\nHistoryPush: VERBOSE\r\nHistoryReplace: VERBOSE\r\nHoldCall: INFO\r\nHoldParticipant: INFO\r\nKickParticipant: INFO\r\nLogout: INFO\r\nMonitorCall: INFO\r\nNavigateToView: INFO\r\nRejectTask: INFO\r\nSelectTask: INFO\r\nSelectTaskInSupervisor: INFO\r\nSelectWorkerInSupervisor: INFO\r\nSendMessage: INFO\r\nSendTyping: VERBOSE\r\nSetActivity: INFO\r\nSetInputText: VERBOSE\r\nShowDirectory: VERBOSE\r\nStopMonitoringCall: INFO\r\nToggleMute: INFO\r\nToggleSidebar: VERBOSE\r\nTransferTask: INFO\r\nUnholdCall: INFO\r\nUnholdParticipant: INFO\r\nWrapupTask: INFO\r\n```\r\n\r\n### Twilio Event Hooks\r\n\r\nCurrently, Twilio only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with ```twilioEventHooks``` option, and use the following functions to send event data to a custom endpoint.\r\n\r\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The data format should be the same as received from Twilio. The content type of data sent by the logger is ```application/x-www-form-urlencoded```.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// unrelated properties omitted\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\r\n\t}\r\n});\r\n\r\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\r\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\r\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\r\n```\r\n\r\nExample Azure Function hook for debugger events:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\nmodule.exports = async function (context, req) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\t}\r\n\t});\r\n\r\n\t// log to console\r\n\tcleLogger.info(\"Received Twilio debugger event\");\r\n\t\r\n\t// send event data to a configured url\r\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\r\n};\r\n```\r\n\r\n### Version\r\n\r\nVersion will be automatically included in all logs.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.version; // 1.0.0\r\n```\r\n\r\n## Log Object\r\n\r\n```js\r\n{\r\n\t// Account sid if provided during init\r\n\taccountSid: string;\r\n\r\n\t// Any additional log data to be included with the log\r\n\tadditionalData: unknown;\r\n\t\r\n\t// Component name, if provided\r\n\tcomponentName: string;\r\n\r\n\t// Generated or configured token\r\n\tcorrelationToken: string;\r\n\r\n\t// Current UTC datetime in ISO 8601 format\r\n\teventDate: string;\r\n\r\n\t// Log level\r\n\tlevel: string;\r\n\r\n\t// Log code, if provided\r\n\tlogCode: number;\r\n\r\n\t// Log message\r\n\tmessage: string;\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\tproperties: object;\r\n\r\n\t// BrowserFunction if the environment is the browser\r\n\t// CloudFunction if the environment is Node.js\r\n\tsource: string;\r\n\r\n\t// Logging library version \r\n\tloggerVersion: string;\r\n}\r\n```\r\n\r\n## Handling PII Data\r\n\r\nThe logger provides support for marking logs as containing PII (personally identifiable data), and configuring each log sink to indicate if it can handle PII data. By default, log sinks are not marked as PII safe, meaning they will not receive logs marked as containing PII. Marking a log sink as PII safe, means that the logger should be able to handle PII data without privacy violations (for example, a remote log sink that sends data to an API that masks PII data would be considered safe).\r\n\r\nAdditionally, the logger exports a function ```tagPII``` which can be used to tag parts of a string message as PII for easier masking. Tags can be used by backend systems to mask the data with a simple regex search and replace.\r\n\r\nExample tagged output:\r\n\r\n```js\r\nimport { tagPII } from \"@prftesp/cle-logger\";\r\n\r\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\r\n```\r\n\r\n## Advanced Configuration\r\n\r\n### Remote logging\r\n\r\nThe library provides a way to provide you own remote log url, or to remotely override the min log level. This can be useful for temporarily setting the log level to verbose in a production environment, for quick troubleshooting.\r\n\r\n**Remote log endpoint**\r\n\r\nTo implement your own remote logging endpoint, all you have to do is accept ```POST``` requests, with a JSON payload of an array of [log objects](#log-object), and return either ```{ success: true }``` or ```{ success: false }```. The endpoint should always return a ```200 OK``` status code. A good use-case would be leveraging a serverless api platform to output logs to a queue.\r\n\r\nExample Azure Function:\r\n\r\n```js\r\nmodule.exports = async function (context, req) {\r\n    context.log(req.body); // body will have the log array\r\n\r\n    context.res = {\r\n        status: 200,\r\n        body: { success: true }\r\n    };\r\n};\r\n```\r\n\r\n**Remote log level override**\r\n\r\nThe ```minLogLevelUrl``` configuration property is used to provide an endpoint that accepts ```GET``` requests and a ```accountSid``` query string parameter. ```accountSid``` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the ```minLogLevel``` configuration property. If null is returned, the ```minLogLevel``` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a ```200 OK``` status code and use the ```success``` property to indicate failure.\r\n\r\nExample success response:\r\n\r\n```json\r\n{ \r\n\t\"success\": true,\r\n\t\"minLogLevel\": \"warn\"\r\n}\r\n```\r\n\r\nExample error response:\r\n\r\n```json\r\n{ \r\n\t\"success\": false,\r\n\t\"minLogLevel\": null\r\n}\r\n```\r\n\r\n## Changelog\r\n\r\n### v2.0.0 [2019-12-20]\r\n\r\n**New features**\r\n- All logging output is now configured using the ```logSinks``` property. The library provides two commonly used loggers - ```consoleLogger``` and a ```remoteLogger``` that accepts the log url as a parameter. [Configuration](#configuration)\r\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\r\n- Log functions have a new optional ```containsPII``` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\r\n- A new helper function ```tagPII``` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\r\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\r\n\r\n**Breaking changes**\r\n- ```customLogSink``` configuration renamed to ```logSinks```\r\n- Removed ```disableRemoteLogging``` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\r\n- Removed ```logToConsole``` configuration. This is now configured as a log sink with a ```consoleLogger``` helper function.\r\n- Removed ```logUrl``` configuration. This is now configured as a log sink with a ```remoteLogger``` helper function.\r\n- Removed ```minLogLevel``` configuration. This is now configured per log sink.\r\n\r\n**Migration guide**\r\n``` javascript\r\nimport cleLogger from \"@prftesp/cle-logger\";\r\n\r\n// v1.0.0 and older config\r\ncleLogger.init({\r\n\tdisableRemoteLogSinks: true, // no direct replacement\r\n\tlogToConsole: true, // replaced by the \"console logger\" sink\r\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\r\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\r\n\tcustomLogSinks: [{\r\n\t\tname: \"custom sink\",\r\n\t\tlog: logs => {/* your custom logging */}\r\n\t}]\r\n});\r\n\r\n// v2.0.0 config\r\ncleLogger.init({\r\n  logSinks: [{\r\n\t\tname: \"custom sink\",\r\n    log: logs => {/* your custom logging */},\r\n    logLevel: cleLogger.logLevels.INFO,\r\n    piiSafe: true // set to true to get feature parity with older version, please consider your use case before setting\r\n  },\r\n  {\r\n    name: \"console logger\",\r\n    log: consoleLogger,\r\n    logLevel: cleLogger.logLevels.INFO,\r\n    piiSafe: true, // set to true to get feature parity with older version, please consider your use case before setting\r\n  },\r\n  {\r\n    name: \"remote logger\",\r\n    log: remoteLogger(\"http://log.output/api\"),\r\n    logLevel: cleLogger.logLevels.INFO,\r\n    piiSafe: true, // set to true to get feature parity with older version, please consider your use case before setting\r\n  }]\r\n});\r\n```\r\n\r\n### v1.0.0 [2019-09-12]\r\n\r\n- Initial release\r\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.0.0-beta.3","_nodeVersion":"10.15.2","_npmVersion":"6.11.3","dist":{"integrity":"sha512-Nn8yqyFPyxqTH0EEUyEX5e0hfHxcqZQpGxZFCUKPxU9Qc18WaxplFB7gjkDsVnAo1+kQF9adXnTd591JhRByyw==","shasum":"1edef2a0a1e731a6aa2a4d758720c8a4d9bb5a71","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.0.0-beta.3.tgz","fileCount":17,"unpackedSize":61708,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd/PwuCRA9TVsSAnZWagAA3IMP/0SkJyL/XTVX38kRvt4X\nUk9Oeg9lS3OPkK2Kex24ZmGwtzAaoYH4PQ6NzQED+uEGZJ90oVICGCp+l+5E\n7SxecLNOcvQAsDxjeVuDOLf5dgRbci4liBAd++AapPr3Fy0EN2rOXMCsSuUV\ntESHXQF0JFVr+bPbhboAv3/s5HlYO8ODEOEmmz3RtB6Iyl0vpbtIskZGyb55\nKlVK3VbNjhECKzYI1K4CmFZz5YrpPivGl+tNRhwspPvFjQexT7ag+wiXXqQi\n6bYYr+yWb45F87uNIWi/NI54jLalwP1YhRPnfwixx1O8N9sKLdI0ODMbSHXN\nh6RwjvTn1cjrYKUALbyXFddLsn9yY8juGEywGMsz3iGHAzSMdlyqJU0GYYjp\nI+bhreEZtBwXi2zpAkMwKwVSr9l2idBdebGm21LBeFMpJBW+YqJdEve6xZHf\nqaaI9U7pUraQ1wjl85cu2u26TL2PDXHCZ7UaZ8/6UvcZebXj/n6oFBPr3XEC\nMvXtVfOcyHTiEEbVBQgZ34ee0GQ1dyv7l2+pQc2KpKhhtIGekTkj+g38kNIw\n7RO1VXl0CZtz7ycq3NDwtS4bgYuAY4CpCHHQxvbmKlf2+c5KVHZzdk3hBpSt\n9UZFIfmF1xXAGj0sRCwX2i9Zkv/GzxmwzjzxEkz81DhSU7Yy7+HsSrR7IR5X\nXL66\r\n=2Lb+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDF6mgkCFG6zK4d+BXOYLFr3N3ijSgt/5cHDgIpxMnlyAiEAtpbpn9iFLDbL12Gs7CyutHLdp4Br98YKg/yQe36DHfI="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.0.0-beta.3_1576860718408_0.550493102693385"},"_hasShrinkwrap":false},"2.0.0-beta.4":{"name":"@prftesp/cle-logger","version":"2.0.0-beta.4","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿CLE Logger\r\n===\r\n\r\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\r\n\r\n## Documentation\r\n\r\n- [Target Environment](#target-environment)\r\n- [Simple Example](#simple-example)\r\n- [Installation](#installation)\r\n- [Features](#features)\r\n- [Configuration](#configuration)\r\n- [API](#api)\r\n\t- [Log sinks](#log-sinks)\r\n\t- [Logging levels](#logging-levels)\r\n\t- [Logging parameters](#logging-parameters)\r\n\t- [Available component names](#available-component-names)\r\n\t- [Available custom property keys](#available-custom-property-keys)\r\n\t- [Log codes](#log-codes)\r\n\t- [Registering Twilio Actions](#registering-twilio-actions)\r\n\t- [Twilio Event Hooks](#twilio-event-hooks)\r\n\t- [Version](#version)\r\n- [Log Object](#log-object)\r\n- [Handling PII Data](#handling-pii-data)\r\n- [Advanced Configuration](#advanced-configuration)\r\n\t- [Remote logging](#remote-logging)\r\n- [Changelog](#changelog)\r\n\r\n## Target Environment\r\n\r\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\r\n\r\n## Simple Example\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"console sink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\n// will log message to console\r\ncleLogger.info(\"log message\");\r\n```\r\n\r\n## Installation\r\n\r\n```bash\r\n$ npm install @prftesp/cle-logger\r\n```\r\n\r\n## Features\r\n\r\n- Runs in Node.js and the browser\r\n- Remote logging\r\n- Multiple log sinks\r\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\r\n- Remote minimum log level overrides\r\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\r\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\r\n- Support for tagging logs with custom component names or correlation tokens\r\n- Send Twilio event hook data to a custom backend\r\n\r\n## Configuration\r\n\r\nTo configure the logger call the ```init``` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\r\n\r\n**Available options**\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// Twilio account sid, required for use with CLE\r\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\r\n\r\n\t// Will be associated with all logs,\r\n\t// if not provided, a unique uuid will be generated\r\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\r\n\t// An array of log sinks that will receive an array of log objects,\r\n\t// and can optionally return a promise\r\n\tlogSinks: [{\r\n\t\t// name of the log sink\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\t// log handler function\r\n\t\tlog: logs => console.log(logs),\r\n\t\t// should this log sink accept PII data, defaults to false\r\n\t\tpiiSafe: true,\r\n\t\t// log level for the sink, defaults to INFO\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n\r\n\t// A way to suppress error messages if set to false, defaults to true\r\n\tlogErrorsToConsole: true,\r\n\r\n\t// If logging Twilio Flex Actions, provide a list of actions \r\n\t// to be excluded from logs\r\n\texcludedFlexActionNames: [\"SendTyping\"],\r\n\r\n\t// A custom endpoint to fetch log level overrides\r\n\t// NOTE: the provided url should always end with 'accountSid=',\r\n\t// the library will append the accountSid\r\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\r\n\r\n\t// An instance of ('@twilio/flex-ui').Manager\r\n\ttwilioFlexManager: manager,\r\n\r\n\t// To send data from Twilio event hooks, to a custom backend,\r\n\t// configure one or more endpoints for each event type\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\r\n\t}\r\n});\r\n```\r\n\r\n## API\r\n\r\nThe logger can be imported as an object in CommonJs or ES6 syntax:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n// or\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n```\r\n\r\nAlternatively, all top level members of the ```cleLogger``` are available as named exports and can be imported separately:\r\n\r\n```js\r\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tlogSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\r\n\r\n// vs\r\n\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tcleLogger.logSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\r\n```\r\n\r\n### Log sinks\r\n\r\nLog sinks can be provided via the ```logSinks``` config. It's an array of objects containing a ```name```, ```piiSafe``` flag to indicate if the sink can handle PII data without concern for privacy violations, ```logLevel```, and a ```log``` method. All provided log sinks will be executed in parallel. You can choose to await the logs to make sure all logging operations are completed.\r\nThe ```log``` method will receive an array of log objects for you to handle. If you're doing async requests in your custom log sinks, it is recommended to return the Promise.\r\n\r\nTwo built-in loggers are available out of the box. ```consoleLogger``` for logging to standard console, and ```remoteLogger``` for sending logs over http(s).\r\n\r\nLog sink object:\r\n\r\n``` js\r\n{\r\n\t// name of the log sink\r\n\tname: \"exampleConsoleSink\",\r\n\t// log handler function\r\n\tlog: logs => console.log(logs),\r\n\t// should this log sink accept PII data, defaults to false\r\n\tpiiSafe: true,\r\n\t// log level for the sink, defaults to INFO\r\n\tlogLevel: cleLogger.logLevels.VERBOSE\r\n}\r\n```\r\n\r\nExample log sinks:\r\n\r\n``` js\r\nconst fs = require(\"fs\");\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"fileSink\",\r\n\t\tlog: logs => {\r\n\t\t\tfs.appendFile(\r\n\t\t\t\t\"./log.txt\",\r\n\t\t\t\t`${JSON.stringify(logs)}`,\r\n\t\t\t\tconsole.log\r\n\t\t\t);\r\n\t\t}\r\n\t},\r\n\t{\r\n\t\tname: \"remoteSink\",\r\n\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\r\n\t\tlogLevel: cleLogger.logLevels.WARN,\r\n\t\tpiiSafe: true\r\n\t},\r\n\t{\r\n\t\tname: \"consoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}]\r\n});\r\n```\r\n\r\n### Logging levels\r\n\r\nLogging level constants can be found in ```cleLogger.logLevels```.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.logLevels.VERBOSE; // verbose\r\ncleLogger.logLevels.INFO; // info\r\ncleLogger.logLevels.WARN; // warn\r\ncleLogger.logLevels.ERROR; // error\r\ncleLogger.logLevels.CRITICAL; // critical\r\n```\r\n\r\nThere are functions available for each supported logging level:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(...);\r\ncleLogger.info(...);\r\ncleLogger.warn(...);\r\ncleLogger.error(...);\r\ncleLogger.critical(...);\r\n```\r\n\r\nThe functions are all async, return ```Promise<void>```, and can be optionally awaited. The suggested approach is to not await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is to suggested to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\r\n\r\n### Logging parameters\r\n\r\nAll logging functions take the same parameters:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(\r\n\t// The only required property, no logs will be generated without it\r\n\t\"Log message\",\r\n\r\n\t// Any additional log data you would like to include with the log,\r\n\t// can be any data type\r\n\t{ data: \"test\" },\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\t// CLE uses it to index these properties in Application Insights\r\n\t// For CLE, try to use keys found in 'cleLogger.customPropertyKeys'\r\n\t{ userId: 123 },\r\n\r\n\t// Another useful way to 'tag' your logs for easier searching\r\n\t// Can be any string, but for CLE, try to use available options in 'cleLogger.componentNames'\r\n\t// If you would like more component names added, please let us know \r\n\t\"ProfileComponent\",\r\n\r\n\t// Log code, can be any number used to categorize logs,\r\n\t// there will be a separate document listing CLE codes\r\n\t70123,\r\n\r\n\t// Correlation token to be associated with this log only,\r\n\t// if one is not provided, the token supplied/generated during 'init' will be used\r\n\t\"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\t\r\n\t// Does the log contain PII data, defaults to false. If set to true, the log will be \r\n\t// passed in only to those log sinks that have 'piiSafe' set to true.\r\n\ttrue,\r\n);\r\n```\r\n\r\n### Available component names\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.componentNames.SESSION;\r\ncleLogger.componentNames.DIALER;\r\ncleLogger.componentNames.DASHBOARD;\r\ncleLogger.componentNames.AFTERCALL;\r\ncleLogger.componentNames.SUPERVISOR;\r\n```\r\n\r\n### Available custom property keys\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.customPropertyKeys.CHANNEL_SID;\r\ncleLogger.customPropertyKeys.TASK_SID;\r\ncleLogger.customPropertyKeys.CALL_SID;\r\ncleLogger.customPropertyKeys.WORKFLOW_SID;\r\ncleLogger.customPropertyKeys.WORKER_SID;\r\n```\r\n\r\n### Log codes\r\n\r\nTo categorize a log in one of the following categories, provide a log code in the appropriate range:\r\n\r\n- 100 000 - 109 999 => CALL_STATUS\r\n- 110 000 - 119 999 => IVR\r\n- 120 000 - 129 999 => ROUTING\r\n- 130 000 - 139 999 => AGENT_ACTION\r\n- 140 000 - 149 999 => TRANSFER\r\n- 150 000 - 159 999 => RM_INTEGRATION\r\n- 160 000 - 169 999 => WFM_INTEGRATION\r\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\r\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\r\n\r\n### Registering Twilio Actions\r\n\r\nExample in a Twilio Flex UI plugin:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\n/**\r\n * @param flex { typeof import('@twilio/flex-ui') }\r\n * @param manager { import('@twilio/flex-ui').Manager }\r\n */\r\ninit(flex, manager) {\r\n\tcleLogger.init({\r\n\t\taccountSid: \"your-account-sid\"\r\n\t});\r\n\r\n\tcleLogger.registerTwilioActions(flex);\r\n}\r\n```\r\n\r\n**Note**: A Twilio account sid must be provided during ```init``` with the ```accountSid``` parameter, otherwise the actions won't be logged.\r\n\r\nThe logger will loop through all actions found under ```flex.Actions.actions```, and attach ```after``` event handlers to log the action name and payload. Any private properties found in the payload (starting with '_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Any actions names configured in ```excludedFlexActionNames``` will not be included in the logs.\r\n\r\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\r\n\r\n**Twilio Actions log levels**\r\n\r\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of ```INFO```.\r\n\r\n``` js\r\nAcceptTask: INFO\r\nCancelTransfer: INFO\r\nCompleteTask: INFO\r\nHangupCall: INFO\r\nHideDirectory: VERBOSE\r\nHistoryGo: VERBOSE\r\nHistoryGoBack: VERBOSE\r\nHistoryGoForward: VERBOSE\r\nHistoryPush: VERBOSE\r\nHistoryReplace: VERBOSE\r\nHoldCall: INFO\r\nHoldParticipant: INFO\r\nKickParticipant: INFO\r\nLogout: INFO\r\nMonitorCall: INFO\r\nNavigateToView: INFO\r\nRejectTask: INFO\r\nSelectTask: INFO\r\nSelectTaskInSupervisor: INFO\r\nSelectWorkerInSupervisor: INFO\r\nSendMessage: INFO\r\nSendTyping: VERBOSE\r\nSetActivity: INFO\r\nSetInputText: VERBOSE\r\nShowDirectory: VERBOSE\r\nStopMonitoringCall: INFO\r\nToggleMute: INFO\r\nToggleSidebar: VERBOSE\r\nTransferTask: INFO\r\nUnholdCall: INFO\r\nUnholdParticipant: INFO\r\nWrapupTask: INFO\r\n```\r\n\r\n### Twilio Event Hooks\r\n\r\nCurrently, Twilio only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with ```twilioEventHooks``` option, and use the following functions to send event data to a custom endpoint.\r\n\r\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The data format should be the same as received from Twilio. The content type of data sent by the logger is ```application/x-www-form-urlencoded```.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// unrelated properties omitted\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\r\n\t}\r\n});\r\n\r\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\r\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\r\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\r\n```\r\n\r\nExample Azure Function hook for debugger events:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\nmodule.exports = async function (context, req) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\t}\r\n\t});\r\n\r\n\t// log to console\r\n\tcleLogger.info(\"Received Twilio debugger event\");\r\n\t\r\n\t// send event data to a configured url\r\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\r\n};\r\n```\r\n\r\n### Version\r\n\r\nVersion will be automatically included in all logs.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.version; // 1.0.0\r\n```\r\n\r\n## Log Object\r\n\r\n```js\r\n{\r\n\t// Account sid if provided during init\r\n\taccountSid: string;\r\n\r\n\t// Any additional log data to be included with the log\r\n\tadditionalData: unknown;\r\n\t\r\n\t// Component name, if provided\r\n\tcomponentName: string;\r\n\r\n\t// Generated or configured token\r\n\tcorrelationToken: string;\r\n\r\n\t// Current UTC datetime in ISO 8601 format\r\n\teventDate: string;\r\n\r\n\t// Log level\r\n\tlevel: string;\r\n\r\n\t// Log code, if provided\r\n\tlogCode: number;\r\n\r\n\t// Log message\r\n\tmessage: string;\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\tproperties: object;\r\n\r\n\t// BrowserFunction if the environment is the browser\r\n\t// CloudFunction if the environment is Node.js\r\n\tsource: string;\r\n\r\n\t// Logging library version \r\n\tloggerVersion: string;\r\n}\r\n```\r\n\r\n## Handling PII Data\r\n\r\nThe logger provides support for marking logs as containing PII (personally identifiable data), and configuring each log sink to indicate if it can handle PII data. By default, log sinks are not marked as PII safe, meaning they will not receive logs marked as containing PII. Marking a log sink as PII safe, means that the logger should be able to handle PII data without privacy violations (for example, a remote log sink that sends data to an API that masks PII data would be considered safe).\r\n\r\nAdditionally, the logger exports a function ```tagPII``` which can be used to tag parts of a string message as PII for easier masking. Tags can be used by backend systems to mask the data with a simple regex search and replace.\r\n\r\nExample tagged output:\r\n\r\n```js\r\nimport { tagPII } from \"@prftesp/cle-logger\";\r\n\r\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\r\n```\r\n\r\n## Advanced Configuration\r\n\r\n### Remote logging\r\n\r\nThe library provides a way to provide you own remote log url, or to remotely override the min log level. This can be useful for temporarily setting the log level to verbose in a production environment, for quick troubleshooting.\r\n\r\n**Remote log endpoint**\r\n\r\nTo implement your own remote logging endpoint, all you have to do is accept ```POST``` requests, with a JSON payload of an array of [log objects](#log-object), and return either ```{ success: true }``` or ```{ success: false }```. The endpoint should always return a ```200 OK``` status code. A good use-case would be leveraging a serverless api platform to output logs to a queue.\r\n\r\nExample Azure Function:\r\n\r\n```js\r\nmodule.exports = async function (context, req) {\r\n    context.log(req.body); // body will have the log array\r\n\r\n    context.res = {\r\n        status: 200,\r\n        body: { success: true }\r\n    };\r\n};\r\n```\r\n\r\n**Remote log level override**\r\n\r\nThe ```minLogLevelUrl``` configuration property is used to provide an endpoint that accepts ```GET``` requests and a ```accountSid``` query string parameter. ```accountSid``` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the ```minLogLevel``` configuration property. If null is returned, the ```minLogLevel``` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a ```200 OK``` status code and use the ```success``` property to indicate failure.\r\n\r\nExample success response:\r\n\r\n```json\r\n{ \r\n\t\"success\": true,\r\n\t\"minLogLevel\": \"warn\"\r\n}\r\n```\r\n\r\nExample error response:\r\n\r\n```json\r\n{ \r\n\t\"success\": false,\r\n\t\"minLogLevel\": null\r\n}\r\n```\r\n\r\n## Changelog\r\n\r\n### v2.0.0 [2019-12-20]\r\n\r\n**New features**\r\n- All logging output is now configured using the ```logSinks``` property. The library provides two commonly used loggers - ```consoleLogger``` and a ```remoteLogger``` that accepts the log url as a parameter. [Configuration](#configuration)\r\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\r\n- Log functions have a new optional ```containsPII``` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\r\n- A new helper function ```tagPII``` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\r\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\r\n\r\n**Breaking changes**\r\n- ```customLogSink``` configuration renamed to ```logSinks```\r\n- Removed ```disableRemoteLogging``` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\r\n- Removed ```logToConsole``` configuration. This is now configured as a log sink with a ```consoleLogger``` helper function.\r\n- Removed ```logUrl``` configuration. This is now configured as a log sink with a ```remoteLogger``` helper function.\r\n- Removed ```minLogLevel``` configuration. This is now configured per log sink.\r\n\r\n**Migration guide**\r\n``` javascript\r\nimport cleLogger from \"@prftesp/cle-logger\";\r\n\r\n// v1.0.0 and older config\r\ncleLogger.init({\r\n\tdisableRemoteLogSinks: true, // no direct replacement\r\n\tlogToConsole: true, // replaced by the \"console logger\" sink\r\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\r\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\r\n\tcustomLogSinks: [{\r\n\t\tname: \"custom sink\",\r\n\t\tlog: logs => {/* your custom logging */}\r\n\t}]\r\n});\r\n\r\n// v2.0.0 config\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"custom sink\",\r\n\t\tlog: logs => {/* your custom logging */},\r\n\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t// set to true to get feature parity with older version,\r\n\t\t// please consider your use case before setting\r\n\t\tpiiSafe: true\r\n\t},\r\n\t{\r\n\t\tname: \"console logger\",\r\n\t\tlog: consoleLogger,\r\n\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t// set to true to get feature parity with older version,\r\n\t\t// please consider your use case before setting\r\n\t\tpiiSafe: true\r\n\t},\r\n\t{\r\n\t\tname: \"remote logger\",\r\n\t\tlog: remoteLogger(\"http://log.output/api\"),\r\n\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t// set to true to get feature parity with older version,\r\n\t\t// please consider your use case before setting\r\n\t\tpiiSafe: true\t\t\r\n\t}]\r\n});\r\n```\r\n\r\n### v1.0.0 [2019-09-12]\r\n\r\n- Initial release\r\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.0.0-beta.4","_nodeVersion":"10.15.2","_npmVersion":"6.11.3","dist":{"integrity":"sha512-KI2y1seDI+Qo874AxuahM7wsQby8CYQ5LY11/ptg4FFnDYTiYzFpkHVXkca2iWlRmWI8OlMAwEI7K8+jKQec5g==","shasum":"6911db7cb0845bbc0fca8c303b2c9240fdbd2ee5","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.0.0-beta.4.tgz","fileCount":17,"unpackedSize":61707,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd/P08CRA9TVsSAnZWagAAzWEP/RMr8fRoFsjZNtklNgac\nPTmT5fwrcWVKQXlAYJWtrfCVZmwUTb+2hf6s63iJ0ylQb+4YXHhpykXC1Fin\n9JHv7NllpCdHoP8FYDa5CgXdarVX4ldRkP5VWt/Tmge157L5tuAyjvQYG/fx\n8iKxU4GGUoyK2niqrxbvmyfGYfEtL96xdqIMR6evgEHuRDEDgdZXSvSpu2bJ\nrZyP2rwJ7uY2qSHlGLZpD08AbzRCdsXxyk392LBobcwbXRdss0FMLK3WE7gN\nB/vnU27P9IdXxNsUqRWCGsvqbi+yr+NEDX8KqNG+V/FWtUcKS9FuudQrEBj1\n9vbF2mGlxfZTHNDRrs9QfrKHCCNQTUtE/RIoSEZ5DH13equGRTZWTt/Qjiz1\nazDLN1bEMjpg6OluhzT9n6sXyrBA7df0ZLdxeLOeh1lHu8oF8CxaiYXH3lMt\n+cOjh4u1w6t8vb2HQjLPdtN1TFOTFTSkNmgTSl492CgK8IZemX/RLJqiUcAW\nd1JQqK9i6I8DXxU9e2E7W7WVMgEz1cmCzqzwHwJvfHX7RYQhi+dzQL5BiP0R\nABQRmXda9gc8leTttHXenfh6nFC1AzNJLUK+Pzy6b3/NIi2FWLu3t/vN7iuy\nRjElhEJ8CVq+WnMAo4w2JvxXACaaBcVQX74E0llurdPEnYkYVRVpCGCeZxnG\nzoG2\r\n=7L10\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD1rhXcZv5WJ+b6aTSS+NOQ/alwXxqzA7QVK4BIU9almQIgHnLhCw/fub5onrKTb9vf2/zk5Jx96r7MKRIpM8LTftU="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.0.0-beta.4_1576860988460_0.17735926405208668"},"_hasShrinkwrap":false},"2.0.0-beta.20200107.3":{"name":"@prftesp/cle-logger","version":"2.0.0-beta.20200107.3","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿CLE Logger\n===\n\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\n\n## Documentation\n\n- [Target Environment](#target-environment)\n- [Simple Example](#simple-example)\n- [Installation](#installation)\n- [Features](#features)\n- [Configuration](#configuration)\n- [API](#api)\n\t- [Log sinks](#log-sinks)\n\t- [Logging levels](#logging-levels)\n\t- [Logging parameters](#logging-parameters)\n\t- [Available component names](#available-component-names)\n\t- [Available custom property keys](#available-custom-property-keys)\n\t- [Log codes](#log-codes)\n\t- [Registering Twilio Actions](#registering-twilio-actions)\n\t- [Twilio Event Hooks](#twilio-event-hooks)\n\t- [Version](#version)\n- [Log Object](#log-object)\n- [Handling PII Data](#handling-pii-data)\n- [Advanced Configuration](#advanced-configuration)\n\t- [Remote logging](#remote-logging)\n- [Changelog](#changelog)\n\n## Target Environment\n\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\n\n## Simple Example\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\tlogSinks: [{\n\t\tname: \"console sink\",\n\t\tlog: cleLogger.consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}],\n});\n\n// will log message to console\ncleLogger.info(\"log message\");\n```\n\n## Installation\n\n```bash\n$ npm install @prftesp/cle-logger\n```\n\n## Features\n\n- Runs in Node.js and the browser\n- Remote logging\n- Multiple log sinks\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\n- Remote minimum log level overrides\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\n- Support for tagging logs with custom component names or correlation tokens\n- Send Twilio event hook data to a custom backend\n\n## Configuration\n\nTo configure the logger call the ```init``` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\n\n**Available options**\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// Twilio account sid, required for use with CLE\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\n\n\t// Will be associated with all logs,\n\t// if not provided, a unique uuid will be generated\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\n\n\t// An array of log sinks that will receive an array of log objects,\n\t// and can optionally return a promise\n\tlogSinks: [{\n\t\t// name of the log sink\n\t\tname: \"exampleConsoleSink\",\n\t\t// log handler function\n\t\tlog: logs => console.log(logs),\n\t\t// should this log sink accept PII data, defaults to false\n\t\tpiiSafe: true,\n\t\t// log level for the sink, defaults to INFO\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}],\n\n\t// A way to suppress error messages if set to false, defaults to true\n\tlogErrorsToConsole: true,\n\n\t// If logging Twilio Flex Actions, provide a list of actions \n\t// to be excluded from logs\n\texcludedFlexActionNames: [\"SendTyping\"],\n\n\t// A custom endpoint to fetch log level overrides\n\t// NOTE: the provided url should always end with 'accountSid=',\n\t// the library will append the accountSid\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\n\n\t// An instance of ('@twilio/flex-ui').Manager\n\ttwilioFlexManager: manager,\n\n\t// To send data from Twilio event hooks, to a custom backend,\n\t// configure one or more endpoints for each event type\n\ttwilioEventHooks: {\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\n\t}\n});\n```\n\n## API\n\nThe logger can be imported as an object in CommonJs or ES6 syntax:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n// or\nconst cleLogger = require(\"@prftesp/cle-logger\");\n```\n\nAlternatively, all top level members of the ```cleLogger``` are available as named exports and can be imported separately:\n\n```js\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\n\ninit({\n\tlogSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: logLevels.VERBOSE\n\t}],\n});\n\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\n\n// vs\n\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ninit({\n\tcleLogger.logSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: cleLogger.consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}],\n});\n\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\n```\n\n### Log sinks\n\nLog sinks can be provided via the ```logSinks``` config. It's an array of objects containing a ```name```, ```piiSafe``` flag to indicate if the sink can handle PII data without concern for privacy violations, ```logLevel```, and a ```log``` method. All provided log sinks will be executed in parallel. You can choose to await the logs to make sure all logging operations are completed.\nThe ```log``` method will receive an array of log objects for you to handle. If you're doing async requests in your custom log sinks, it is recommended to return the Promise.\n\nTwo built-in loggers are available out of the box. ```consoleLogger``` for logging to standard console, and ```remoteLogger``` for sending logs over http(s).\n\nLog sink object:\n\n``` js\n{\n\t// name of the log sink\n\tname: \"exampleConsoleSink\",\n\t// log handler function\n\tlog: logs => console.log(logs),\n\t// should this log sink accept PII data, defaults to false\n\tpiiSafe: true,\n\t// log level for the sink, defaults to INFO\n\tlogLevel: cleLogger.logLevels.VERBOSE\n}\n```\n\nExample log sinks:\n\n``` js\nconst fs = require(\"fs\");\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\ncleLogger.init({\n\tlogSinks: [{\n\t\tname: \"fileSink\",\n\t\tlog: logs => {\n\t\t\tfs.appendFile(\n\t\t\t\t\"./log.txt\",\n\t\t\t\t`${JSON.stringify(logs)}`,\n\t\t\t\tconsole.log\n\t\t\t);\n\t\t}\n\t},\n\t{\n\t\tname: \"remoteSink\",\n\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\n\t\tlogLevel: cleLogger.logLevels.WARN,\n\t\tpiiSafe: true\n\t},\n\t{\n\t\tname: \"consoleSink\",\n\t\tlog: cleLogger.consoleLogger,\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}]\n});\n```\n\n### Logging levels\n\nLogging level constants can be found in ```cleLogger.logLevels```.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.logLevels.VERBOSE; // verbose\ncleLogger.logLevels.INFO; // info\ncleLogger.logLevels.WARN; // warn\ncleLogger.logLevels.ERROR; // error\ncleLogger.logLevels.CRITICAL; // critical\n```\n\nThere are functions available for each supported logging level:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(...);\ncleLogger.info(...);\ncleLogger.warn(...);\ncleLogger.error(...);\ncleLogger.critical(...);\n```\n\nThe functions are all async, return ```Promise<void>```, and can be optionally awaited. The suggested approach is to not await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is to suggested to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\n\n### Logging parameters\n\nAll logging functions take the same parameters:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(\n\t// The only required property, no logs will be generated without it\n\t\"Log message\",\n\n\t// Any additional log data you would like to include with the log,\n\t// can be any data type\n\t{ data: \"test\" },\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\t// CLE uses it to index these properties in Application Insights\n\t// For CLE, try to use keys found in 'cleLogger.customPropertyKeys'\n\t{ userId: 123 },\n\n\t// Another useful way to 'tag' your logs for easier searching\n\t// Can be any string, but for CLE, try to use available options in 'cleLogger.componentNames'\n\t// If you would like more component names added, please let us know \n\t\"ProfileComponent\",\n\n\t// Log code, can be any number used to categorize logs,\n\t// there will be a separate document listing CLE codes\n\t70123,\n\n\t// Correlation token to be associated with this log only,\n\t// if one is not provided, the token supplied/generated during 'init' will be used\n\t\"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\n\t\n\t// Does the log contain PII data, defaults to false. If set to true, the log will be \n\t// passed in only to those log sinks that have 'piiSafe' set to true.\n\ttrue,\n);\n```\n\n### Available component names\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.componentNames.SESSION;\ncleLogger.componentNames.DIALER;\ncleLogger.componentNames.DASHBOARD;\ncleLogger.componentNames.AFTERCALL;\ncleLogger.componentNames.SUPERVISOR;\n```\n\n### Available custom property keys\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.customPropertyKeys.CHANNEL_SID;\ncleLogger.customPropertyKeys.TASK_SID;\ncleLogger.customPropertyKeys.CALL_SID;\ncleLogger.customPropertyKeys.WORKFLOW_SID;\ncleLogger.customPropertyKeys.WORKER_SID;\n```\n\n### Log codes\n\nTo categorize a log in one of the following categories, provide a log code in the appropriate range:\n\n- 100 000 - 109 999 => CALL_STATUS\n- 110 000 - 119 999 => IVR\n- 120 000 - 129 999 => ROUTING\n- 130 000 - 139 999 => AGENT_ACTION\n- 140 000 - 149 999 => TRANSFER\n- 150 000 - 159 999 => RM_INTEGRATION\n- 160 000 - 169 999 => WFM_INTEGRATION\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\n\n### Registering Twilio Actions\n\nExample in a Twilio Flex UI plugin:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n/**\n * @param flex { typeof import('@twilio/flex-ui') }\n * @param manager { import('@twilio/flex-ui').Manager }\n */\ninit(flex, manager) {\n\tcleLogger.init({\n\t\taccountSid: \"your-account-sid\"\n\t});\n\n\tcleLogger.registerTwilioActions(flex);\n}\n```\n\n**Note**: A Twilio account sid must be provided during ```init``` with the ```accountSid``` parameter, otherwise the actions won't be logged.\n\nThe logger will loop through all actions found under ```flex.Actions.actions```, and attach ```after``` event handlers to log the action name and payload. Any private properties found in the payload (starting with '_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Any actions names configured in ```excludedFlexActionNames``` will not be included in the logs.\n\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\n\n**Twilio Actions log levels**\n\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of ```INFO```.\n\n``` js\nAcceptTask: INFO\nCancelTransfer: INFO\nCompleteTask: INFO\nHangupCall: INFO\nHideDirectory: VERBOSE\nHistoryGo: VERBOSE\nHistoryGoBack: VERBOSE\nHistoryGoForward: VERBOSE\nHistoryPush: VERBOSE\nHistoryReplace: VERBOSE\nHoldCall: INFO\nHoldParticipant: INFO\nKickParticipant: INFO\nLogout: INFO\nMonitorCall: INFO\nNavigateToView: INFO\nRejectTask: INFO\nSelectTask: INFO\nSelectTaskInSupervisor: INFO\nSelectWorkerInSupervisor: INFO\nSendMessage: INFO\nSendTyping: VERBOSE\nSetActivity: INFO\nSetInputText: VERBOSE\nShowDirectory: VERBOSE\nStopMonitoringCall: INFO\nToggleMute: INFO\nToggleSidebar: VERBOSE\nTransferTask: INFO\nUnholdCall: INFO\nUnholdParticipant: INFO\nWrapupTask: INFO\n```\n\n### Twilio Event Hooks\n\nCurrently, Twilio only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with ```twilioEventHooks``` option, and use the following functions to send event data to a custom endpoint.\n\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is ```application/x-www-form-urlencoded``` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// unrelated properties omitted\n\ttwilioEventHooks: {\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\n\t}\n});\n\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\n```\n\nExample Azure Function hook for debugger events:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\nmodule.exports = async function (context, req) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\n\t\t}\n\t});\n\n\t// log to console\n\tcleLogger.info(\"Received Twilio debugger event\");\n\t\n\t// send event data to a configured url\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\n};\n```\n\n**NOTE:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to ```x-www-form-urlencoded``` format.\n\nExample Twilio function task router relay using the ```form-urlencoded``` npm package to convert the data to ```x-www-form-urlencoded``` format:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\nconst formurlencoded = require('form-urlencoded').default;\n\nexports.handler = async function(context, event, callback) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\n\t\t}\n\t});\n\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\n\n\tcallback();\n};\n```\n\n### Version\n\nVersion will be automatically included in all logs.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.version; // 1.0.0\n```\n\n## Log Object\n\n```js\n{\n\t// Account sid if provided during init\n\taccountSid: string;\n\n\t// Any additional log data to be included with the log\n\tadditionalData: unknown;\n\t\n\t// Component name, if provided\n\tcomponentName: string;\n\n\t// Generated or configured token\n\tcorrelationToken: string;\n\n\t// Current UTC datetime in ISO 8601 format\n\teventDate: string;\n\n\t// Log level\n\tlevel: string;\n\n\t// Log code, if provided\n\tlogCode: number;\n\n\t// Log message\n\tmessage: string;\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\tproperties: object;\n\n\t// BrowserFunction if the environment is the browser\n\t// CloudFunction if the environment is Node.js\n\tsource: string;\n\n\t// Logging library version \n\tloggerVersion: string;\n}\n```\n\n## Handling PII Data\n\nThe logger provides support for marking logs as containing PII (personally identifiable data), and configuring each log sink to indicate if it can handle PII data. By default, log sinks are not marked as PII safe, meaning they will not receive logs marked as containing PII. Marking a log sink as PII safe, means that the logger should be able to handle PII data without privacy violations (for example, a remote log sink that sends data to an API that masks PII data would be considered safe).\n\nAdditionally, the logger exports a function ```tagPII``` which can be used to tag parts of a string message as PII for easier masking. Tags can be used by backend systems to mask the data with a simple regex search and replace.\n\nExample tagged output:\n\n```js\nimport { tagPII } from \"@prftesp/cle-logger\";\n\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\n```\n\n## Advanced Configuration\n\n### Remote logging\n\nThe library provides a way to provide you own remote log url, or to remotely override the min log level. This can be useful for temporarily setting the log level to verbose in a production environment, for quick troubleshooting.\n\n**Remote log endpoint**\n\nTo implement your own remote logging endpoint, all you have to do is accept ```POST``` requests, with a JSON payload of an array of [log objects](#log-object), and return either ```{ success: true }``` or ```{ success: false }```. The endpoint should always return a ```200 OK``` status code. A good use-case would be leveraging a serverless api platform to output logs to a queue.\n\nExample Azure Function:\n\n```js\nmodule.exports = async function (context, req) {\n    context.log(req.body); // body will have the log array\n\n    context.res = {\n        status: 200,\n        body: { success: true }\n    };\n};\n```\n\n**Remote log level override**\n\nThe ```minLogLevelUrl``` configuration property is used to provide an endpoint that accepts ```GET``` requests and a ```accountSid``` query string parameter. ```accountSid``` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the ```minLogLevel``` configuration property. If null is returned, the ```minLogLevel``` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a ```200 OK``` status code and use the ```success``` property to indicate failure.\n\nExample success response:\n\n```json\n{ \n\t\"success\": true,\n\t\"minLogLevel\": \"warn\"\n}\n```\n\nExample error response:\n\n```json\n{ \n\t\"success\": false,\n\t\"minLogLevel\": null\n}\n```\n\n## Changelog\n\n### v2.0.0 [2019-12-20]\n\n**New features**\n- All logging output is now configured using the ```logSinks``` property. The library provides two commonly used loggers - ```consoleLogger``` and a ```remoteLogger``` that accepts the log url as a parameter. [Configuration](#configuration)\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\n- Log functions have a new optional ```containsPII``` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\n- A new helper function ```tagPII``` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\n\n**Breaking changes**\n- ```customLogSink``` configuration renamed to ```logSinks```\n- Removed ```disableRemoteLogging``` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\n- Removed ```logToConsole``` configuration. This is now configured as a log sink with a ```consoleLogger``` helper function.\n- Removed ```logUrl``` configuration. This is now configured as a log sink with a ```remoteLogger``` helper function.\n- Removed ```minLogLevel``` configuration. This is now configured per log sink.\n\n**Migration guide**\n``` javascript\nimport cleLogger from \"@prftesp/cle-logger\";\n\n// v1.0.0 and older config\ncleLogger.init({\n\tdisableRemoteLogSinks: true, // no direct replacement\n\tlogToConsole: true, // replaced by the \"console logger\" sink\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\n\tcustomLogSinks: [{\n\t\tname: \"custom sink\",\n\t\tlog: logs => {/* your custom logging */}\n\t}]\n});\n\n// v2.0.0 config\ncleLogger.init({\n\tlogSinks: [{\n\t\tname: \"custom sink\",\n\t\tlog: logs => {/* your custom logging */},\n\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t// set to true to get feature parity with older version,\n\t\t// please consider your use case before setting\n\t\tpiiSafe: true\n\t},\n\t{\n\t\tname: \"console logger\",\n\t\tlog: consoleLogger,\n\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t// set to true to get feature parity with older version,\n\t\t// please consider your use case before setting\n\t\tpiiSafe: true\n\t},\n\t{\n\t\tname: \"remote logger\",\n\t\tlog: remoteLogger(\"http://log.output/api\"),\n\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t// set to true to get feature parity with older version,\n\t\t// please consider your use case before setting\n\t\tpiiSafe: true\t\t\n\t}]\n});\n```\n\n### v1.0.0 [2019-09-12]\n\n- Initial release\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.0.0-beta.20200107.3","_nodeVersion":"12.13.1","_npmVersion":"6.13.2","dist":{"integrity":"sha512-hclUSLAnFR4+5AjUpicBxkHHAvjHnyUHVkDeCC/6E3Q1D6k6oWJB70xkToSjd1Slm6zi8iHQPgkPZXURxbR1SA==","shasum":"2349fc6f84a007e7525f708d8f4fec0caf6447e9","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.0.0-beta.20200107.3.tgz","fileCount":17,"unpackedSize":61387,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeFLoZCRA9TVsSAnZWagAA52AP/3NhitmqAoJ3QRGoM1Q9\ndcxfWyy+1EY0Fw/u2OEfxW/Q8qKSmhSX+GP8rhpepws/g5Bg7anNDvI/pZIq\nc0X030v/hzNNMEl4yeqjuzpgkomo1ggqn+sBY3/zsE/+F+LNPKLLM97ruVYx\nam4pyFNFma86uk5PJjRXVPjS48jSONoqK3XU9Hh69aMAIZFyl3PNS9u6WIIf\nwzQ18DMdDxNa9HQSICH5eRl9tPM0+6xXSy2YkV/JVTdecQncDyI+j1lBjfOd\nBFTuqwG73zdhFS3xtvetrnwMQ3JvDXlgFPwQG110GEbr5Y/ubFnFIm1nxVzI\n+nL226W4+/eRqo2lttvSey5E5rFTcUyjPScbdTphywUDYq2gY/5NBaG9Or1n\n3lZZIxbUot/8Ss3DsrJhpG7NqWkrzbxjxRe1TYsjBffpHG4D1N+9OyRtsXPU\nzwQWZ8KTXLT6uo1T3MYnkg/lQDNgmFJTyCE44kIEJ7MyLVWSivVrWaTH8mMh\nZJNykhiGWDM8Vx8pHHcvhkav30R0pcx0tw8M76bv6Adz6G+bxhn2+VO6G9Nk\nVedysF5IAbv6n/9iBwnjQSzf8LTX9wOTeF/jrzTPDDPX6vHJSZZ9MptHYVaR\n2sBF+TcEbxsTVskHweFxI2emLgpmyLuVx9xVzvVmjO7bc/5a+xxw/x7KolAo\nbC7k\r\n=zaU1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDxpRR+UQR4JSFar9qLZYQNBoCMgoJ0lnadwOxzUJ9/sAiEA/atc0tZK1ce2nwmnIvORArMtgsoXnxRtAfPvUAwzXjE="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.0.0-beta.20200107.3_1578416664519_0.5636304428010734"},"_hasShrinkwrap":false},"2.0.0":{"name":"@prftesp/cle-logger","version":"2.0.0","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@2.0.0","_nodeVersion":"12.13.1","_npmVersion":"6.13.2","dist":{"integrity":"sha512-4X3cHIGQDNaLFUBQkAdteCFugD8Vqf6Lprm4R6Ps26+xA+KCqGf7Fn+fKMw6dumuwX6TWta+HZMbZ887MjhyDQ==","shasum":"bba96b4db5aaee0af0b1e33290265ec213603d3f","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.0.0.tgz","fileCount":17,"unpackedSize":61288,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeFMZuCRA9TVsSAnZWagAA2+oP/isx3HHqmmoh70WxBu6u\nDoeXYQxr2oiJ058yvkYymfWhgbzrevkqSOhAnBABAbvoLPGZN0aU7KEzd1Xt\nq/ST6y6Jk5vdo05fgv7C6ycIxgxmyIGDXkFzubVcgWN4yf63JPnxokweizSZ\nVkZsmTyM1ZkF1wEXwwL6n6x9qkMulieo0PVHbeomWlBMgY+zBgFA6EPkvcMF\nUcd0MvMuTtt2X6AwR9y/7Fe78W3wHsDUr+Qe88F2tCsLC4ZKVHaOkbfT55WY\n8pe0rE9KpHTLrY1f9nY9WGiJyqMWYVbfhxA13LIZPnZl0qhyjnwErLGeYjSg\nM5QRMOWm55xauF//GCKrgRnyju92Z/E5Q+lsyjGwSd8kGoyvYJp65lbeI0W5\nHfI5tQOo4nFFXtmq0L8Z7flskPVep9Z6tj0S6spgTB3GhS+5Gzt/bKVsEFPm\n3VClh53LP2L6+dWA0HbQZH540XskkNj0J9M6TXrAmcBzcUNZzAXooE1fS0YC\nUl19YUP88wJidoJ3yi01YWvqOLxpkh4DJaU2D5hi85iNzF+1d51QzJ/1Ys/S\n2y10rk0siDUD3BJ/P57LpAUQTRWR20Kg2swbGCiZCqWhcajVwU1MxYzmqNWj\nA8mrcF8rKnUkNwY4dP3ZXLLjIMx/1rpQfotbPTonv6lzBcqLe/K3z1cB+jm8\ntXqc\r\n=SR6H\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDFZxtfjEDxGYSpjaD1e9uyfBIxwrMcwNrHxV9Cc7i6xQIhAO2mGAmzUpZPqqtl9FS36YPwHVqpXK1g8KdGMbs8xmfD"}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.0.0_1578419821919_0.14321723482746207"},"_hasShrinkwrap":false},"2.0.1-beta.20200116.2":{"name":"@prftesp/cle-logger","version":"2.0.1-beta.20200116.2","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿CLE Logger\n===\n\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\n\n## Documentation\n\n- [Target Environment](#target-environment)\n- [Simple Example](#simple-example)\n- [Installation](#installation)\n- [Features](#features)\n- [Configuration](#configuration)\n- [API](#api)\n\t- [Log sinks](#log-sinks)\n\t- [Logging levels](#logging-levels)\n\t- [Logging parameters](#logging-parameters)\n\t- [Available component names](#available-component-names)\n\t- [Available custom property keys](#available-custom-property-keys)\n\t- [Log codes](#log-codes)\n\t- [Registering Twilio Actions](#registering-twilio-actions)\n\t- [Twilio Event Hooks](#twilio-event-hooks)\n\t- [Version](#version)\n- [Log Object](#log-object)\n- [Handling PII Data](#handling-pii-data)\n- [Advanced Configuration](#advanced-configuration)\n\t- [Remote logging](#remote-logging)\n- [Changelog](#changelog)\n\n## Target Environment\n\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\n\n## Simple Example\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\tlogSinks: [{\n\t\tname: \"console sink\",\n\t\tlog: cleLogger.consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}],\n});\n\n// will log message to console\ncleLogger.info(\"log message\");\n```\n\n## Installation\n\n```bash\n$ npm install @prftesp/cle-logger\n```\n\n## Features\n\n- Runs in Node.js and the browser\n- Remote logging\n- Multiple log sinks\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\n- Remote minimum log level overrides\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\n- Support for tagging logs with custom component names or correlation tokens\n- Send Twilio event hook data to a custom backend\n\n## Configuration\n\nTo configure the logger call the ```init``` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\n\n**Available options**\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// Twilio account sid, required for use with CLE\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\n\n\t// Will be associated with all logs,\n\t// if not provided, a unique uuid will be generated\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\n\n\t// An array of log sinks that will receive an array of log objects,\n\t// and can optionally return a promise\n\tlogSinks: [{\n\t\t// name of the log sink\n\t\tname: \"exampleConsoleSink\",\n\t\t// log handler function\n\t\tlog: cleLogger.consoleLogger,\n\t\t// should this log sink accept PII data, defaults to false\n\t\tpiiSafe: true,\n\t\t// log level for the sink, defaults to INFO\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}],\n\n\t// A way to suppress error messages if set to false, defaults to true\n\tlogErrorsToConsole: true,\n\n\t// If logging Twilio Flex Actions, provide a list of actions \n\t// to be excluded from logs\n\texcludedFlexActionNames: [\"SendTyping\"],\n\n\t// A custom endpoint to fetch log level overrides\n\t// NOTE: the provided url should always end with 'accountSid=',\n\t// the library will append the accountSid\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\n\n\t// An instance of ('@twilio/flex-ui').Manager\n\ttwilioFlexManager: manager,\n\n\t// To send data from Twilio event hooks, to a custom backend,\n\t// configure one or more endpoints for each event type\n\ttwilioEventHooks: {\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\n\t}\n});\n```\n\n## API\n\nThe logger can be imported as an object in CommonJs or ES6 syntax:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n// or\nconst cleLogger = require(\"@prftesp/cle-logger\");\n```\n\nAlternatively, all top level members of the ```cleLogger``` are available as named exports and can be imported separately:\n\n```js\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\n\ninit({\n\tlogSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: logLevels.VERBOSE\n\t}],\n});\n\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\n\n// vs\n\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ninit({\n\tcleLogger.logSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: cleLogger.consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}],\n});\n\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\n```\n\n### Log sinks\n\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the ```logSinks``` config. It's an array of objects containing a ```name```, ```piiSafe``` flag to indicate if the sink can handle PII data without concern for privacy violations, ```logLevel```, and a ```log``` method. The ```log``` method will receive an array of log objects for you to handle. All provided log sinks will be executed in parallel. Log sinks should be async operations, and must return a Promise object. Built-in logger already do this, if you have a custom logger, make sure to return a promise.\n\nTwo built-in loggers are available out of the box. ```consoleLogger``` for logging to standard console, and ```remoteLogger``` for sending logs over http(s).\n\nLog sink object:\n\n``` js\n{\n\t// name of the log sink\n\tname: \"exampleConsoleSink\",\n\t// log handler function\n\tlog: logs => { \n\t\tconsole.log(logs);\n\t\treturn Promise.resolve();\n\t},\n\t// should this log sink accept PII data, defaults to false\n\tpiiSafe: true,\n\t// log level for the sink, defaults to INFO\n\tlogLevel: cleLogger.logLevels.VERBOSE\n}\n```\n\nExample log sinks:\n\n``` js\nconst fs = require(\"fs\");\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\ncleLogger.init({\n\tlogSinks: [{\n\t\tname: \"fileSink\",\n\t\tlog: logs => {\n\t\t\tfs.appendFile(\n\t\t\t\t\"./log.txt\",\n\t\t\t\t`${JSON.stringify(logs)}`,\n\t\t\t\tconsole.log\n\t\t\t);\n\t\t\treturn Promise.resolve();\n\t\t}\n\t},\n\t{\n\t\tname: \"remoteSink\",\n\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\n\t\tlogLevel: cleLogger.logLevels.WARN,\n\t\tpiiSafe: true\n\t},\n\t{\n\t\tname: \"consoleSink\",\n\t\tlog: cleLogger.consoleLogger,\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}]\n});\n```\n\n### Logging levels\n\nLogging level constants can be found in ```cleLogger.logLevels```.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.logLevels.VERBOSE; // verbose\ncleLogger.logLevels.INFO; // info\ncleLogger.logLevels.WARN; // warn\ncleLogger.logLevels.ERROR; // error\ncleLogger.logLevels.CRITICAL; // critical\n```\n\nThere are functions available for each supported logging level:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(...);\ncleLogger.info(...);\ncleLogger.warn(...);\ncleLogger.error(...);\ncleLogger.critical(...);\n```\n\nThe functions are all async, return ```Promise<void>```, and can be optionally awaited. The suggested approach is to not await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is to suggested to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\n\n### Logging parameters\n\nAll logging functions take the same parameters:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(\n\t// The only required property, no logs will be generated without it\n\t\"Log message\",\n\n\t// Any additional log data you would like to include with the log,\n\t// can be any data type\n\t{ data: \"test\" },\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\t// CLE uses it to index these properties in Application Insights\n\t// For CLE, try to use keys found in 'cleLogger.customPropertyKeys'\n\t{ userId: 123 },\n\n\t// Another useful way to 'tag' your logs for easier searching\n\t// Can be any string, but for CLE, try to use available options in 'cleLogger.componentNames'\n\t// If you would like more component names added, please let us know \n\t\"ProfileComponent\",\n\n\t// Log code, can be any number used to categorize logs,\n\t// there will be a separate document listing CLE codes\n\t70123,\n\n\t// Correlation token to be associated with this log only,\n\t// if one is not provided, the token supplied/generated during 'init' will be used\n\t\"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\n\t\n\t// Does the log contain PII data, defaults to false. If set to true, the log will be \n\t// passed in only to those log sinks that have 'piiSafe' set to true.\n\ttrue,\n);\n```\n\n### Available component names\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.componentNames.SESSION;\ncleLogger.componentNames.DIALER;\ncleLogger.componentNames.DASHBOARD;\ncleLogger.componentNames.AFTERCALL;\ncleLogger.componentNames.SUPERVISOR;\n```\n\n### Available custom property keys\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.customPropertyKeys.CHANNEL_SID;\ncleLogger.customPropertyKeys.TASK_SID;\ncleLogger.customPropertyKeys.CALL_SID;\ncleLogger.customPropertyKeys.WORKFLOW_SID;\ncleLogger.customPropertyKeys.WORKER_SID;\n```\n\n### Log codes\n\nTo categorize a log in one of the following categories, provide a log code in the appropriate range:\n\n- 100 000 - 109 999 => CALL_STATUS\n- 110 000 - 119 999 => IVR\n- 120 000 - 129 999 => ROUTING\n- 130 000 - 139 999 => AGENT_ACTION\n- 140 000 - 149 999 => TRANSFER\n- 150 000 - 159 999 => RM_INTEGRATION\n- 160 000 - 169 999 => WFM_INTEGRATION\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\n\n### Registering Twilio Actions\n\nExample in a Twilio Flex UI plugin:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n/**\n * @param flex { typeof import('@twilio/flex-ui') }\n * @param manager { import('@twilio/flex-ui').Manager }\n */\ninit(flex, manager) {\n\tcleLogger.init({\n\t\taccountSid: \"your-account-sid\"\n\t});\n\n\tcleLogger.registerTwilioActions(flex);\n}\n```\n\n**Note**: A Twilio account sid must be provided during ```init``` with the ```accountSid``` parameter, otherwise the actions won't be logged.\n\nThe logger will loop through all actions found under ```flex.Actions.actions```, and attach ```after``` event handlers to log the action name and payload. Any private properties found in the payload (starting with '_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Any actions names configured in ```excludedFlexActionNames``` will not be included in the logs.\n\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\n\n**Twilio Actions log levels**\n\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of ```INFO```.\n\n``` js\nAcceptTask: INFO\nCancelTransfer: INFO\nCompleteTask: INFO\nHangupCall: INFO\nHideDirectory: VERBOSE\nHistoryGo: VERBOSE\nHistoryGoBack: VERBOSE\nHistoryGoForward: VERBOSE\nHistoryPush: VERBOSE\nHistoryReplace: VERBOSE\nHoldCall: INFO\nHoldParticipant: INFO\nKickParticipant: INFO\nLogout: INFO\nMonitorCall: INFO\nNavigateToView: INFO\nRejectTask: INFO\nSelectTask: INFO\nSelectTaskInSupervisor: INFO\nSelectWorkerInSupervisor: INFO\nSendMessage: INFO\nSendTyping: VERBOSE\nSetActivity: INFO\nSetInputText: VERBOSE\nShowDirectory: VERBOSE\nStopMonitoringCall: INFO\nToggleMute: INFO\nToggleSidebar: VERBOSE\nTransferTask: INFO\nUnholdCall: INFO\nUnholdParticipant: INFO\nWrapupTask: INFO\n```\n\n### Twilio Event Hooks\n\nCurrently, Twilio only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with ```twilioEventHooks``` option, and use the following functions to send event data to a custom endpoint.\n\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is ```application/x-www-form-urlencoded``` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// unrelated properties omitted\n\ttwilioEventHooks: {\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\n\t}\n});\n\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\n```\n\nExample Azure Function hook for debugger events:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\nmodule.exports = async function (context, req) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\n\t\t}\n\t});\n\n\t// log to console\n\tcleLogger.info(\"Received Twilio debugger event\");\n\t\n\t// send event data to a configured url\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\n};\n```\n\n**NOTE:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to ```x-www-form-urlencoded``` format.\n\nExample Twilio function task router relay using the ```form-urlencoded``` npm package to convert the data to ```x-www-form-urlencoded``` format:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\nconst formurlencoded = require('form-urlencoded').default;\n\nexports.handler = async function(context, event, callback) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\n\t\t}\n\t});\n\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\n\n\tcallback();\n};\n```\n\n### Version\n\nVersion will be automatically included in all logs.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.version; // 1.0.0\n```\n\n## Log Object\n\n```js\n{\n\t// Account sid if provided during init\n\taccountSid: string;\n\n\t// Any additional log data to be included with the log\n\tadditionalData: unknown;\n\t\n\t// Component name, if provided\n\tcomponentName: string;\n\n\t// Generated or configured token\n\tcorrelationToken: string;\n\n\t// Current UTC datetime in ISO 8601 format\n\teventDate: string;\n\n\t// Log level\n\tlevel: string;\n\n\t// Log code, if provided\n\tlogCode: number;\n\n\t// Log message\n\tmessage: string;\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\tproperties: object;\n\n\t// BrowserFunction if the environment is the browser\n\t// CloudFunction if the environment is Node.js\n\tsource: string;\n\n\t// Logging library version \n\tloggerVersion: string;\n}\n```\n\n## Handling PII Data\n\nThe logger provides support for marking logs as containing PII (personally identifiable data), and configuring each log sink to indicate if it can handle PII data. By default, log sinks are not marked as PII safe, meaning they will not receive logs marked as containing PII. Marking a log sink as PII safe, means that the logger should be able to handle PII data without privacy violations (for example, a remote log sink that sends data to an API that masks PII data would be considered safe).\n\nAdditionally, the logger exports a function ```tagPII``` which can be used to tag parts of a string message as PII for easier masking. Tags can be used by backend systems to mask the data with a simple regex search and replace.\n\nExample tagged output:\n\n```js\nimport { tagPII } from \"@prftesp/cle-logger\";\n\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\n```\n\n## Advanced Configuration\n\n### Remote logging\n\nThe library provides a way to provide you own remote log url, or to remotely override the min log level. This can be useful for temporarily setting the log level to verbose in a production environment, for quick troubleshooting.\n\n**Remote log endpoint**\n\nTo implement your own remote logging endpoint, all you have to do is accept ```POST``` requests, with a JSON payload of an array of [log objects](#log-object), and return either ```{ success: true }``` or ```{ success: false }```. The endpoint should always return a ```200 OK``` status code. A good use-case would be leveraging a serverless api platform to output logs to a queue.\n\nExample Azure Function:\n\n```js\nmodule.exports = async function (context, req) {\n    context.log(req.body); // body will have the log array\n\n    context.res = {\n        status: 200,\n        body: { success: true }\n    };\n};\n```\n\n**Remote log level override**\n\nThe ```minLogLevelUrl``` configuration property is used to provide an endpoint that accepts ```GET``` requests and a ```accountSid``` query string parameter. ```accountSid``` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the ```minLogLevel``` configuration property. If null is returned, the ```minLogLevel``` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a ```200 OK``` status code and use the ```success``` property to indicate failure.\n\nExample success response:\n\n```json\n{ \n\t\"success\": true,\n\t\"minLogLevel\": \"warn\"\n}\n```\n\nExample error response:\n\n```json\n{ \n\t\"success\": false,\n\t\"minLogLevel\": null\n}\n```\n\n## Changelog\n\n### v2.0.0 [2019-12-20]\n\n**New features**\n- All logging output is now configured using the ```logSinks``` property. The library provides two commonly used loggers - ```consoleLogger``` and a ```remoteLogger``` that accepts the log url as a parameter. [Configuration](#configuration)\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\n- Log functions have a new optional ```containsPII``` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\n- A new helper function ```tagPII``` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\n\n**Breaking changes**\n- ```customLogSink``` configuration renamed to ```logSinks```\n- Removed ```disableRemoteLogging``` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\n- Removed ```logToConsole``` configuration. This is now configured as a log sink with a ```consoleLogger``` helper function.\n- Removed ```logUrl``` configuration. This is now configured as a log sink with a ```remoteLogger``` helper function.\n- Removed ```minLogLevel``` configuration. This is now configured per log sink.\n\n**Migration guide**\n``` javascript\nimport cleLogger from \"@prftesp/cle-logger\";\n\n// v1.0.0 and older config\ncleLogger.init({\n\tdisableRemoteLogSinks: true, // no direct replacement\n\tlogToConsole: true, // replaced by the \"console logger\" sink\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\n\tcustomLogSinks: [{\n\t\tname: \"custom sink\",\n\t\tlog: logs => {/* your custom logging */}\n\t}]\n});\n\n// v2.0.0 config\ncleLogger.init({\n\tlogSinks: [{\n\t\tname: \"custom sink\",\n\t\tlog: logs => {/* your custom logging */},\n\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t// set to true to get feature parity with older version,\n\t\t// please consider your use case before setting\n\t\tpiiSafe: true\n\t},\n\t{\n\t\tname: \"console logger\",\n\t\tlog: consoleLogger,\n\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t// set to true to get feature parity with older version,\n\t\t// please consider your use case before setting\n\t\tpiiSafe: true\n\t},\n\t{\n\t\tname: \"remote logger\",\n\t\tlog: remoteLogger(\"http://log.output/api\"),\n\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t// set to true to get feature parity with older version,\n\t\t// please consider your use case before setting\n\t\tpiiSafe: true\t\t\n\t}]\n});\n```\n\n### v1.0.0 [2019-09-12]\n\n- Initial release\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.0.1-beta.20200116.2","_nodeVersion":"12.14.0","_npmVersion":"6.13.4","dist":{"integrity":"sha512-89xW4TDRLEfAaZajX692/4MFhV4WLk5kWgKvaTPksa8wPsAizxdvY6M2rPPN8+6ZTRmwGbXqBZRSz/IIFZ49Ew==","shasum":"776ff1f1709d1ecd9477d55c2fa468630d761cff","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.0.1-beta.20200116.2.tgz","fileCount":17,"unpackedSize":61541,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeIJVtCRA9TVsSAnZWagAA7lUQAJxyGt0/nFasfZxUhOCS\nkjQ34uTlQIWTAGkAK19sVsTyFZX1J135LL58ccA0Y5jJCWxd5NeDOts0Aljz\nQC+S8GXneNflHmy90eEOiG2PZkAdjBhKPBI0zCHVDv1pcbBb7ceoAtsV0S3O\nwCO+A17Zl7WWTfKFxuYdQqwe+ON4o1aiSqIH4Dz9lLMFq66Vr4OaUVj7bjhU\nPB43s5AjwiCgr/9Dr0XE6ChhimwPQujbVta1+qCbNAle+6FrtdP2QEc8FwPg\nyISJVGD/tpUiIU4Bjd+S8iCrHDrhzOOWzkOqVjvzzrj7dHsKc4T0xSjFClZK\nVDr0A5oPokTT/RrcWvs8NVTtHqavsijPnCnZaRhCuWYeeQqhewJ6arA1I7E8\nJyUxQCoBHC1QlPUSAvEF3yE0lOGBuPn/2Zob4tDq+QQoUQG7Po0oPeE8Y92l\naz2LsADBZMCacvTJed5mlh9TusBF+pYnxEj+ei8mP5uYeMwVuY4K0c6BFMMo\nHNog6opLzI3pPXV5jPDiph2k1njoKfUZjUK/9ADyJXNXc6orY65sTBYRbx8D\nO5Jw2ISfAb+HNYsXo16QUKw/EQbUE69Ub/1EX0EDZY9bbx4mxRGXneJUaXmK\nt5UqntizElzcGENXbYyPgFGlxZBX+UTHL0LmKub+GPAEEL/MvQasC4eaEFM+\n2gq5\r\n=iUMr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDm7umlt0czwxwa/14JiueARGC6TT2SOJdrvj6BTzHjaQIgFuRX0qk3RsyVBje8dEde+7qnVyFKUYm76QOmd+iG6ek="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.0.1-beta.20200116.2_1579193709283_0.4392884440179119"},"_hasShrinkwrap":false},"2.0.1":{"name":"@prftesp/cle-logger","version":"2.0.1","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@2.0.1","_nodeVersion":"12.14.0","_npmVersion":"6.13.4","dist":{"integrity":"sha512-tdcMFeNBKraXMGH0bkeKJMyVYjiWNyPqjVIpVZsfLe1rj69Etpvnw+DYeh93B7QeJ1ZPwy70cHW83NPBwte3NA==","shasum":"67692bc441474baae7e68f955bcfbe676379ac90","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.0.1.tgz","fileCount":17,"unpackedSize":61442,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeIJXMCRA9TVsSAnZWagAAgccP/iLQ5AsTXrcomXq6bIkX\ne59uXpr2UTtN7AI6hGFdnuZUzMKSVtKExDlpHRS9Z69nCuE16f+bRThNrRXQ\ncRyWBMCbxP2j/zTj2X7l6vTsrahKVp3FFUesWA3wBWMqjbqUiZ5mOErbaoma\ne1xJlxROxiuQK5zbNloAKumG83ZrODA9kfbzgtQ3Iljp7IFG3xEzHLPZKBgm\nO5Zt3B29RF43y03xieieuWsnHqROF0VGq7ejOuX8bgwH4VeVrmJyel79BQyH\np+AGq5CaV4Zt+2PczFUzxza+q+wEePx5oSwiX5ZBpj+kmTMysPDNZnTwYv1F\n65bFlWaXxluODlr2a41sRmWQQe9kg/Kn7+2Xg9bqhI0lXx4PvsCmyptu5SBV\nMkYTGBzmgBuCxB9QWBgkky2F7wnozvuklculnJlJA9IQtWW52/9vE5z7YsMO\n3PQkw15u6UWRRI2m55Q/fxl7gcf47lKsz2lRR4w4qTfkoHMR2jF3nTnyD/Yh\nuvU02sATWnvGcFAKly299fZ5SfG7uNLpp4daYS+RCZPOMCWil0lqEFKOsRei\n5l3VYsDOJbSH3xscghFjlsddi2UlsxtTjW+34pRZCcD+Kf0NZcfBqt7XGyz1\nJpPkLwHALdSRS2LcSfDHRZeLAl9bmCOiVq5FGURHk5557svy5bJzJ1ii3Sx7\nrMCM\r\n=DD3D\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCEkBXbqPw9V5ZCGQJXn4QI824eTwHPkxjSUYs/RDsbVwIhAKGV/hmBNgFaE7xBioeJohY/fpr/B0jibndpHw85Gh1J"}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.0.1_1579193803863_0.47258827724253716"},"_hasShrinkwrap":false},"2.0.2-beta.20200117.1":{"name":"@prftesp/cle-logger","version":"2.0.2-beta.20200117.1","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿CLE Logger\n===\n\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\n\n## Documentation\n\n- [Target Environment](#target-environment)\n- [Simple Example](#simple-example)\n- [Installation](#installation)\n- [Features](#features)\n- [Configuration](#configuration)\n- [API](#api)\n\t- [Log sinks](#log-sinks)\n\t- [Logging levels](#logging-levels)\n\t- [Logging parameters](#logging-parameters)\n\t- [Available component names](#available-component-names)\n\t- [Available custom property keys](#available-custom-property-keys)\n\t- [Log codes](#log-codes)\n\t- [Registering Twilio Actions](#registering-twilio-actions)\n\t- [Twilio Event Hooks](#twilio-event-hooks)\n\t- [Version](#version)\n- [Log Object](#log-object)\n- [Handling PII Data](#handling-pii-data)\n- [Advanced Configuration](#advanced-configuration)\n\t- [Remote logging](#remote-logging)\n- [Changelog](#changelog)\n\n## Target Environment\n\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\n\n## Simple Example\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\tlogSinks: [{\n\t\tname: \"console sink\",\n\t\tlog: cleLogger.consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}],\n});\n\n// will log message to console\ncleLogger.info(\"log message\");\n```\n\n## Installation\n\n```bash\n$ npm install @prftesp/cle-logger\n```\n\n## Features\n\n- Runs in Node.js and the browser\n- Remote logging\n- Multiple log sinks\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\n- Remote minimum log level overrides\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\n- Support for tagging logs with custom component names or correlation tokens\n- Send Twilio event hook data to a custom backend\n\n## Configuration\n\nTo configure the logger call the ```init``` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\n\n**Available options**\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// Twilio account sid, required for use with CLE\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\n\n\t// Will be associated with all logs,\n\t// if not provided, a unique uuid will be generated\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\n\n\t// An array of log sinks that will receive an array of log objects,\n\t// and can optionally return a promise\n\tlogSinks: [{\n\t\t// name of the log sink\n\t\tname: \"exampleConsoleSink\",\n\t\t// log handler function\n\t\tlog: cleLogger.consoleLogger,\n\t\t// should this log sink accept PII data, defaults to false\n\t\tpiiSafe: true,\n\t\t// log level for the sink, defaults to INFO\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}],\n\n\t// A way to suppress error messages if set to false, defaults to true\n\tlogErrorsToConsole: true,\n\n\t// If logging Twilio Flex Actions, provide a list of actions \n\t// to be excluded from logs\n\texcludedFlexActionNames: [\"SendTyping\"],\n\n\t// A custom endpoint to fetch log level overrides\n\t// NOTE: the provided url should always end with 'accountSid=',\n\t// the library will append the accountSid\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\n\n\t// An instance of ('@twilio/flex-ui').Manager\n\ttwilioFlexManager: manager,\n\n\t// To send data from Twilio event hooks, to a custom backend,\n\t// configure one or more endpoints for each event type\n\ttwilioEventHooks: {\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\n\t}\n});\n```\n\n## API\n\nThe logger can be imported as an object in CommonJs or ES6 syntax:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n// or\nconst cleLogger = require(\"@prftesp/cle-logger\");\n```\n\nAlternatively, all top level members of the ```cleLogger``` are available as named exports and can be imported separately:\n\n```js\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\n\ninit({\n\tlogSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: logLevels.VERBOSE\n\t}],\n});\n\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\n\n// vs\n\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ninit({\n\tcleLogger.logSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: cleLogger.consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}],\n});\n\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\n```\n\n### Log sinks\n\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the ```logSinks``` config. It's an array of objects containing a ```name```, ```piiSafe``` flag to indicate if the sink can handle PII data without concern for privacy violations, ```logLevel```, and a ```log``` method. The ```log``` method will receive an array of log objects for you to handle. All provided log sinks will be executed in parallel. Log sinks should be async operations, and must return a Promise object. Built-in logger already do this, if you have a custom logger, make sure to return a promise.\n\nTwo built-in loggers are available out of the box. ```consoleLogger``` for logging to standard console, and ```remoteLogger``` for sending logs over http(s).\n\nLog sink object:\n\n``` js\n{\n\t// name of the log sink\n\tname: \"exampleConsoleSink\",\n\t// log handler function\n\tlog: logs => { \n\t\tconsole.log(logs);\n\t\treturn Promise.resolve();\n\t},\n\t// should this log sink accept PII data, defaults to false\n\tpiiSafe: true,\n\t// log level for the sink, defaults to INFO\n\tlogLevel: cleLogger.logLevels.VERBOSE\n}\n```\n\nExample log sinks:\n\n``` js\nconst fs = require(\"fs\");\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\ncleLogger.init({\n\tlogSinks: [{\n\t\tname: \"fileSink\",\n\t\tlog: logs => {\n\t\t\tfs.appendFile(\n\t\t\t\t\"./log.txt\",\n\t\t\t\t`${JSON.stringify(logs)}`,\n\t\t\t\tconsole.log\n\t\t\t);\n\t\t\treturn Promise.resolve();\n\t\t}\n\t},\n\t{\n\t\tname: \"remoteSink\",\n\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\n\t\tlogLevel: cleLogger.logLevels.WARN,\n\t\tpiiSafe: true\n\t},\n\t{\n\t\tname: \"consoleSink\",\n\t\tlog: cleLogger.consoleLogger,\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}]\n});\n```\n\n### Logging levels\n\nLogging level constants can be found in ```cleLogger.logLevels```.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.logLevels.VERBOSE; // verbose\ncleLogger.logLevels.INFO; // info\ncleLogger.logLevels.WARN; // warn\ncleLogger.logLevels.ERROR; // error\ncleLogger.logLevels.CRITICAL; // critical\n```\n\nThere are functions available for each supported logging level:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(...);\ncleLogger.info(...);\ncleLogger.warn(...);\ncleLogger.error(...);\ncleLogger.critical(...);\n```\n\nThe functions are all async, return ```Promise<void>```, and can be optionally awaited. The suggested approach is to not await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is to suggested to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\n\n### Logging parameters\n\nAll logging functions take the same parameters:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(\n\t// The only required property, no logs will be generated without it\n\t\"Log message\",\n\n\t// Any additional log data you would like to include with the log,\n\t// can be any data type\n\t{ data: \"test\" },\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\t// CLE uses it to index these properties in Application Insights\n\t// For CLE, try to use keys found in 'cleLogger.customPropertyKeys'\n\t{ userId: 123 },\n\n\t// Another useful way to 'tag' your logs for easier searching\n\t// Can be any string, but for CLE, try to use available options in 'cleLogger.componentNames'\n\t// If you would like more component names added, please let us know \n\t\"ProfileComponent\",\n\n\t// Log code, can be any number used to categorize logs,\n\t// there will be a separate document listing CLE codes\n\t70123,\n\n\t// Correlation token to be associated with this log only,\n\t// if one is not provided, the token supplied/generated during 'init' will be used\n\t\"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\n\t\n\t// Does the log contain PII data, defaults to false. If set to true, the log will be \n\t// passed in only to those log sinks that have 'piiSafe' set to true.\n\ttrue,\n);\n```\n\n### Available component names\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.componentNames.SESSION;\ncleLogger.componentNames.DIALER;\ncleLogger.componentNames.DASHBOARD;\ncleLogger.componentNames.AFTERCALL;\ncleLogger.componentNames.SUPERVISOR;\n```\n\n### Available custom property keys\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.customPropertyKeys.CHANNEL_SID;\ncleLogger.customPropertyKeys.TASK_SID;\ncleLogger.customPropertyKeys.CALL_SID;\ncleLogger.customPropertyKeys.WORKFLOW_SID;\ncleLogger.customPropertyKeys.WORKER_SID;\n```\n\n### Log codes\n\nTo categorize a log in one of the following categories, provide a log code in the appropriate range:\n\n- 100 000 - 109 999 => CALL_STATUS\n- 110 000 - 119 999 => IVR\n- 120 000 - 129 999 => ROUTING\n- 130 000 - 139 999 => AGENT_ACTION\n- 140 000 - 149 999 => TRANSFER\n- 150 000 - 159 999 => RM_INTEGRATION\n- 160 000 - 169 999 => WFM_INTEGRATION\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\n\n### Registering Twilio Actions\n\nExample in a Twilio Flex UI plugin:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n/**\n * @param flex { typeof import('@twilio/flex-ui') }\n * @param manager { import('@twilio/flex-ui').Manager }\n */\ninit(flex, manager) {\n\tcleLogger.init({\n\t\taccountSid: \"your-account-sid\"\n\t});\n\n\tcleLogger.registerTwilioActions(flex);\n}\n```\n\n**Note**: A Twilio account sid must be provided during ```init``` with the ```accountSid``` parameter, otherwise the actions won't be logged.\n\nThe logger will loop through all actions found under ```flex.Actions.actions```, and attach ```after``` event handlers to log the action name and payload. Any private properties found in the payload (starting with '_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Any actions names configured in ```excludedFlexActionNames``` will not be included in the logs.\n\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\n\n**Twilio Actions log levels**\n\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of ```INFO```.\n\n``` js\nAcceptTask: INFO\nCancelTransfer: INFO\nCompleteTask: INFO\nHangupCall: INFO\nHideDirectory: VERBOSE\nHistoryGo: VERBOSE\nHistoryGoBack: VERBOSE\nHistoryGoForward: VERBOSE\nHistoryPush: VERBOSE\nHistoryReplace: VERBOSE\nHoldCall: INFO\nHoldParticipant: INFO\nKickParticipant: INFO\nLogout: INFO\nMonitorCall: INFO\nNavigateToView: INFO\nRejectTask: INFO\nSelectTask: INFO\nSelectTaskInSupervisor: INFO\nSelectWorkerInSupervisor: INFO\nSendMessage: INFO\nSendTyping: VERBOSE\nSetActivity: INFO\nSetInputText: VERBOSE\nShowDirectory: VERBOSE\nStopMonitoringCall: INFO\nToggleMute: INFO\nToggleSidebar: VERBOSE\nTransferTask: INFO\nUnholdCall: INFO\nUnholdParticipant: INFO\nWrapupTask: INFO\n```\n\n### Twilio Event Hooks\n\nCurrently, Twilio only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with ```twilioEventHooks``` option, and use the following functions to send event data to a custom endpoint.\n\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is ```application/x-www-form-urlencoded``` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// unrelated properties omitted\n\ttwilioEventHooks: {\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\n\t}\n});\n\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\n```\n\nExample Azure Function hook for debugger events:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\nmodule.exports = async function (context, req) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\n\t\t}\n\t});\n\n\t// log to console\n\tcleLogger.info(\"Received Twilio debugger event\");\n\t\n\t// send event data to a configured url\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\n};\n```\n\n**NOTE:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to ```x-www-form-urlencoded``` format.\n\nExample Twilio function task router relay using the ```form-urlencoded``` npm package to convert the data to ```x-www-form-urlencoded``` format:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\nconst formurlencoded = require('form-urlencoded').default;\n\nexports.handler = async function(context, event, callback) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\n\t\t}\n\t});\n\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\n\n\tcallback();\n};\n```\n\n### Version\n\nVersion will be automatically included in all logs.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.version; // 1.0.0\n```\n\n## Log Object\n\n```js\n{\n\t// Account sid if provided during init\n\taccountSid: string;\n\n\t// Any additional log data to be included with the log\n\tadditionalData: unknown;\n\t\n\t// Component name, if provided\n\tcomponentName: string;\n\n\t// Generated or configured token\n\tcorrelationToken: string;\n\n\t// Current UTC datetime in ISO 8601 format\n\teventDate: string;\n\n\t// Log level\n\tlevel: string;\n\n\t// Log code, if provided\n\tlogCode: number;\n\n\t// Log message\n\tmessage: string;\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\tproperties: object;\n\n\t// BrowserFunction if the environment is the browser\n\t// CloudFunction if the environment is Node.js\n\tsource: string;\n\n\t// Logging library version \n\tloggerVersion: string;\n}\n```\n\n## Handling PII Data\n\nThe logger provides support for marking logs as containing PII (personally identifiable data), and configuring each log sink to indicate if it can handle PII data. By default, log sinks are not marked as PII safe, meaning they will not receive logs marked as containing PII. Marking a log sink as PII safe, means that the logger should be able to handle PII data without privacy violations (for example, a remote log sink that sends data to an API that masks PII data would be considered safe).\n\nAdditionally, the logger exports a function ```tagPII``` which can be used to tag parts of a string message as PII for easier masking. Tags can be used by backend systems to mask the data with a simple regex search and replace.\n\nExample tagged output:\n\n```js\nimport { tagPII } from \"@prftesp/cle-logger\";\n\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\n```\n\n## Advanced Configuration\n\n### Remote logging\n\nThe library provides a way to provide you own remote log url, or to remotely override the min log level. This can be useful for temporarily setting the log level to verbose in a production environment, for quick troubleshooting.\n\n**Remote log endpoint**\n\nTo implement your own remote logging endpoint, all you have to do is accept ```POST``` requests, with a JSON payload of an array of [log objects](#log-object), and return either ```{ success: true }``` or ```{ success: false }```. The endpoint should always return a ```200 OK``` status code. A good use-case would be leveraging a serverless api platform to output logs to a queue.\n\nExample Azure Function:\n\n```js\nmodule.exports = async function (context, req) {\n    context.log(req.body); // body will have the log array\n\n    context.res = {\n        status: 200,\n        body: { success: true }\n    };\n};\n```\n\n**Remote log level override**\n\nThe ```minLogLevelUrl``` configuration property is used to provide an endpoint that accepts ```GET``` requests and a ```accountSid``` query string parameter. ```accountSid``` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the ```minLogLevel``` configuration property. If null is returned, the ```minLogLevel``` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a ```200 OK``` status code and use the ```success``` property to indicate failure.\n\nExample success response:\n\n```json\n{ \n\t\"success\": true,\n\t\"minLogLevel\": \"warn\"\n}\n```\n\nExample error response:\n\n```json\n{ \n\t\"success\": false,\n\t\"minLogLevel\": null\n}\n```\n\n## Changelog\n\n### v2.0.0 [2019-12-20]\n\n**New features**\n- All logging output is now configured using the ```logSinks``` property. The library provides two commonly used loggers - ```consoleLogger``` and a ```remoteLogger``` that accepts the log url as a parameter. [Configuration](#configuration)\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\n- Log functions have a new optional ```containsPII``` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\n- A new helper function ```tagPII``` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\n\n**Breaking changes**\n- ```customLogSink``` configuration renamed to ```logSinks```\n- Removed ```disableRemoteLogging``` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\n- Removed ```logToConsole``` configuration. This is now configured as a log sink with a ```consoleLogger``` helper function.\n- Removed ```logUrl``` configuration. This is now configured as a log sink with a ```remoteLogger``` helper function.\n- Removed ```minLogLevel``` configuration. This is now configured per log sink.\n\n**Migration guide**\n``` javascript\nimport cleLogger from \"@prftesp/cle-logger\";\n\n// v1.0.0 and older config\ncleLogger.init({\n\tdisableRemoteLogSinks: true, // no direct replacement\n\tlogToConsole: true, // replaced by the \"console logger\" sink\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\n\tcustomLogSinks: [{\n\t\tname: \"custom sink\",\n\t\tlog: logs => {/* your custom logging */}\n\t}]\n});\n\n// v2.0.0 config\ncleLogger.init({\n\tlogSinks: [{\n\t\tname: \"custom sink\",\n\t\tlog: logs => {/* your custom logging */},\n\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t// set to true to get feature parity with older version,\n\t\t// please consider your use case before setting\n\t\tpiiSafe: true\n\t},\n\t{\n\t\tname: \"console logger\",\n\t\tlog: consoleLogger,\n\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t// set to true to get feature parity with older version,\n\t\t// please consider your use case before setting\n\t\tpiiSafe: true\n\t},\n\t{\n\t\tname: \"remote logger\",\n\t\tlog: remoteLogger(\"http://log.output/api\"),\n\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t// set to true to get feature parity with older version,\n\t\t// please consider your use case before setting\n\t\tpiiSafe: true\t\t\n\t}]\n});\n```\n\n### v1.0.0 [2019-09-12]\n\n- Initial release\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.0.2-beta.20200117.1","_nodeVersion":"12.14.0","_npmVersion":"6.13.4","dist":{"integrity":"sha512-lb4pQvbq7xML//y3sP+qtLmzTneeIMRthGJLgPHi44LI+GOEunplF8KpVNG70KvqdLfVzqvbtw/vbDz3Gl25rA==","shasum":"1b49109f509b24ceab1c9e815a62261ebe0067a9","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.0.2-beta.20200117.1.tgz","fileCount":17,"unpackedSize":61895,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeIc3/CRA9TVsSAnZWagAAi3MP/jds/pviP0PG4K9Gs+bJ\n1qVXVmT1b+D2Fy7Gf72Jn3qlrjV6YRVOlBPop2YDuAP9tGcovJc9Zigts6bO\ndH4zI+2agJ2mVutPqSc/RBpxjgrEBahTDBL2sCtBCsBXTnT4EVH9+NUAOaO6\nhGQ3phKO58+Uqam983Re257moM/Bkj1T1zwfucxMfEi/Z4U90e+UY9JsNqJL\nzsms40FB9fBDWagh8aivssL3q6ZcaLIiW9Qf2QsjWY5FG4qcqznAS+ZZz7wI\nFigcZ1LmyJnElpfJfnVzB1LGdAQcfNdPVC8udjWlY4tea2mhHTWu2rYERwIq\nrBwxF/kiHbQ3hiqd+jHhdHs6YQheTz98Oh/5Qp24xWU0hzPuDNyrILy/WHXD\n9som5CO35ZcrC4FmEw63f2CFdKnAlkZJTePSzDaw6kcmRt5ewwoMDL9oNkzZ\n+hyv6iKf9q22Lw5DK00LoqW1+4d1EspmuZ0QXXCMCDUXTiGARLr29USGy3f4\nfysmPfoz2dltvcVk39IMAA+n3Ikfj8YpdqcNHaqmNX6u/X8JaL0be8+0t33+\nWdgt9tXV7m2tNSLG8USg512AhlqEg2rCd21Kt/9IYZsrmmECdZlcOgfj8NGA\nxzUCQQ0A0JCaD/mVCzMhlKxlCav3FcRTS0jyhjMowdLDAMRr6/8MIFWGkgWS\n6u9q\r\n=KH/c\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFo5LPyaGc2BoxntH0tOfTRqyS8O99r+vLUxf/EfG220AiBbl3tDzaxz7HuZ3HMorVabFdwHmuW52k1Sqhz0cGhPtQ=="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.0.2-beta.20200117.1_1579273726952_0.22207295722076958"},"_hasShrinkwrap":false},"2.0.2":{"name":"@prftesp/cle-logger","version":"2.0.2","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@2.0.2","_nodeVersion":"12.14.0","_npmVersion":"6.13.4","dist":{"integrity":"sha512-9js9033cVN0LNbAUkMusZ1JNbqXXx6/jaNPCB+EESJbKYDDBF6lIhVIvql17VMR/vnqbwM9SR30WWKAttiELLg==","shasum":"0e5081521a50bd2e8cf9921a509c25d8ad341c22","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.0.2.tgz","fileCount":17,"unpackedSize":61796,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeIc+vCRA9TVsSAnZWagAAuEgP/irqSU2HTbiLiw6z1dX0\nQlDmwEiy6T/Zvk0GufN3APsOvd1ZVzQEzDM55hY6WVP/ivtFsRkWgdHsyT7r\nPlkUsAjMKy7ghueb17cJrNykUm5O/QwLmRNdwne1NMOz4BMSinbg9uvgP3iH\nrBS2guKoLVQsASLASkcQhn7iAbQGbgwcOmlSuOZbZx662g+7Bil4EgmAVy/U\nujTBAf+afz5QnlCNtnoj9cLvhFBitiTi7m/Maa+U2WFaa0F/+k9YAlV9XAn/\nMiFTt8v3Tm9+V02WORDKseaoex8rM5F0PABkp9UqX2BP1kwpELvYaFlczhyY\n0XiweudBRGmMAQ320dY9u0jbMBFaW2UeqXIwIQUOQo32BxQld0pVMTg3nJch\nlhr5ViqcZuENhfnuaNYd6Pxek+OkR+wkkMNN4cftXp1++oZ9rZRR4WbJ6wNX\nFQa6s3eyYTeoPLpXUNRNvsavFPw1kphksMvV08EYkONjUVBbYd/zfBMYPlAP\nIxwwQyY6Pyv+qFgRVn6hs1DQcBnRhSeZq7+LKwKYPzVYA0itgvBMlhy8uqhK\nYZLqLIX47GvZVn6bHoKwTGL4sBJbMaq6e55TIMzd6CNMIUyUJDEzv3IW2tFb\nDykvSlt47jPPFTrAcxk/mJrtr7hn5KurIkObMlMIO2SMmCY96G84uLEdnU7b\nmiHh\r\n=Jp5M\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDWINB6QJczsSp7GRLgN6doWK/2KERZCWIr2RkngGJ02QIhANNGS/GKSjL+uwY5d1QbPckw1+gPDXsOIKZK/4Icn1qS"}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.0.2_1579274158932_0.7188846107757234"},"_hasShrinkwrap":false},"2.0.3-beta.20200130.2":{"name":"@prftesp/cle-logger","version":"2.0.3-beta.20200130.2","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿CLE Logger\n===\n\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\n\n## Documentation\n\n- [Target Environment](#target-environment)\n- [Simple Example](#simple-example)\n- [Installation](#installation)\n- [Features](#features)\n- [Configuration](#configuration)\n- [API](#api)\n\t- [Log sinks](#log-sinks)\n\t- [Logging levels](#logging-levels)\n\t- [Logging parameters](#logging-parameters)\n\t- [Available component names](#available-component-names)\n\t- [Available custom property keys](#available-custom-property-keys)\n\t- [Log codes](#log-codes)\n\t- [Registering Twilio Actions](#registering-twilio-actions)\n\t- [Twilio Event Hooks](#twilio-event-hooks)\n\t- [Version](#version)\n- [Log Object](#log-object)\n- [Handling PII Data](#handling-pii-data)\n- [Advanced Configuration](#advanced-configuration)\n\t- [Remote logging](#remote-logging)\n- [Changelog](#changelog)\n\n## Target Environment\n\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\n\n## Simple Example\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\tlogSinks: [{\n\t\tname: \"console sink\",\n\t\tlog: cleLogger.consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}],\n});\n\n// will log message to console\ncleLogger.info(\"log message\");\n```\n\n## Installation\n\n```bash\n$ npm install @prftesp/cle-logger\n```\n\n## Features\n\n- Runs in Node.js and the browser\n- Remote logging\n- Multiple log sinks\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\n- Remote minimum log level overrides\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\n- Support for tagging logs with custom component names or correlation tokens\n- Send Twilio event hook data to a custom backend\n\n## Configuration\n\nTo configure the logger call the ```init``` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\n\n**Available options**\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// Twilio account sid, required for use with CLE\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\n\n\t// Will be associated with all logs,\n\t// if not provided, a unique uuid will be generated\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\n\n\t// An array of log sinks that will receive an array of log objects,\n\t// and can optionally return a promise\n\tlogSinks: [{\n\t\t// name of the log sink\n\t\tname: \"exampleConsoleSink\",\n\t\t// log handler function\n\t\tlog: cleLogger.consoleLogger,\n\t\t// should this log sink accept PII data, defaults to false\n\t\tpiiSafe: true,\n\t\t// log level for the sink, defaults to INFO\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}],\n\n\t// A way to suppress error messages if set to false, defaults to true\n\tlogErrorsToConsole: true,\n\n\t// If logging Twilio Flex Actions, provide a list of actions \n\t// to be excluded from logs\n\texcludedFlexActionNames: [\"SendTyping\"],\n\n\t// A custom endpoint to fetch log level overrides\n\t// NOTE: the provided url should always end with 'accountSid=',\n\t// the library will append the accountSid\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\n\n\t// An instance of ('@twilio/flex-ui').Manager\n\ttwilioFlexManager: manager,\n\n\t// To send data from Twilio event hooks, to a custom backend,\n\t// configure one or more endpoints for each event type\n\ttwilioEventHooks: {\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\n\t}\n});\n```\n\n## API\n\nThe logger can be imported as an object in CommonJs or ES6 syntax:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n// or\nconst cleLogger = require(\"@prftesp/cle-logger\");\n```\n\nAlternatively, all top level members of the ```cleLogger``` are available as named exports and can be imported separately:\n\n```js\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\n\ninit({\n\tlogSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: logLevels.VERBOSE\n\t}],\n});\n\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\n\n// vs\n\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ninit({\n\tcleLogger.logSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: cleLogger.consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}],\n});\n\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\n```\n\n### Log sinks\n\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the ```logSinks``` config. It's an array of objects containing a ```name```, ```piiSafe``` flag to indicate if the sink can handle PII data without concern for privacy violations, ```logLevel```, and a ```log``` method. The ```log``` method will receive an array of log objects for you to handle. All provided log sinks will be executed in parallel. Log sinks should be async operations, and must return a Promise object. Built-in logger already do this, if you have a custom logger, make sure to return a promise.\n\nTwo built-in loggers are available out of the box. ```consoleLogger``` for logging to standard console, and ```remoteLogger``` for sending logs over http(s).\n\nLog sink object:\n\n``` js\n{\n\t// name of the log sink\n\tname: \"exampleConsoleSink\",\n\t// log handler function\n\tlog: logs => { \n\t\tconsole.log(logs);\n\t\treturn Promise.resolve();\n\t},\n\t// should this log sink accept PII data, defaults to false\n\tpiiSafe: true,\n\t// log level for the sink, defaults to INFO\n\tlogLevel: cleLogger.logLevels.VERBOSE\n}\n```\n\nExample log sinks:\n\n``` js\nconst fs = require(\"fs\");\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\ncleLogger.init({\n\tlogSinks: [{\n\t\tname: \"fileSink\",\n\t\tlog: logs => {\n\t\t\tfs.appendFile(\n\t\t\t\t\"./log.txt\",\n\t\t\t\t`${JSON.stringify(logs)}`,\n\t\t\t\tconsole.log\n\t\t\t);\n\t\t\treturn Promise.resolve();\n\t\t}\n\t},\n\t{\n\t\tname: \"remoteSink\",\n\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\n\t\tlogLevel: cleLogger.logLevels.WARN,\n\t\tpiiSafe: true\n\t},\n\t{\n\t\tname: \"consoleSink\",\n\t\tlog: cleLogger.consoleLogger,\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}]\n});\n```\n\n### Logging levels\n\nLogging level constants can be found in ```cleLogger.logLevels```.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.logLevels.VERBOSE; // verbose\ncleLogger.logLevels.INFO; // info\ncleLogger.logLevels.WARN; // warn\ncleLogger.logLevels.ERROR; // error\ncleLogger.logLevels.CRITICAL; // critical\n```\n\nThere are functions available for each supported logging level:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(...);\ncleLogger.info(...);\ncleLogger.warn(...);\ncleLogger.error(...);\ncleLogger.critical(...);\n```\n\nThe functions are all async, return ```Promise<void>```, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\n\n### Logging parameters\n\nAll logging functions take the same parameters:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(\n\t// The only required property, no logs will be generated without it\n\t\"Log message\",\n\n\t// Any additional log data you would like to include with the log,\n\t// can be any data type\n\t{ data: \"test\" },\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\t// CLE uses it to index these properties in Application Insights\n\t// For CLE, try to use keys found in 'cleLogger.customPropertyKeys'\n\t{ userId: 123 },\n\n\t// Another useful way to 'tag' your logs for easier searching\n\t// Can be any string, but for CLE, try to use available options in 'cleLogger.componentNames'\n\t// If you would like more component names added, please let us know \n\t\"ProfileComponent\",\n\n\t// Log code, can be any number used to categorize logs,\n\t// there will be a separate document listing CLE codes\n\t70123,\n\n\t// Correlation token to be associated with this log only,\n\t// if one is not provided, the token supplied/generated during 'init' will be used\n\t\"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\n\t\n\t// Does the log contain PII data, defaults to false. If set to true, the log will be \n\t// passed in only to those log sinks that have 'piiSafe' set to true.\n\ttrue,\n);\n```\n\n### Available component names\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.componentNames.SESSION;\ncleLogger.componentNames.DIALER;\ncleLogger.componentNames.DASHBOARD;\ncleLogger.componentNames.AFTERCALL;\ncleLogger.componentNames.SUPERVISOR;\n```\n\n### Available custom property keys\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.customPropertyKeys.CHANNEL_SID;\ncleLogger.customPropertyKeys.TASK_SID;\ncleLogger.customPropertyKeys.CALL_SID;\ncleLogger.customPropertyKeys.WORKFLOW_SID;\ncleLogger.customPropertyKeys.WORKER_SID;\n```\n\n### Log codes\n\nTo categorize a log in one of the following categories, provide a log code in the appropriate range:\n\n- 100 000 - 109 999 => CALL_STATUS\n- 110 000 - 119 999 => IVR\n- 120 000 - 129 999 => ROUTING\n- 130 000 - 139 999 => AGENT_ACTION\n- 140 000 - 149 999 => TRANSFER\n- 150 000 - 159 999 => RM_INTEGRATION\n- 160 000 - 169 999 => WFM_INTEGRATION\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\n\n### Registering Twilio Actions\n\nExample in a Twilio Flex UI plugin:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n/**\n * @param flex { typeof import('@twilio/flex-ui') }\n * @param manager { import('@twilio/flex-ui').Manager }\n */\ninit(flex, manager) {\n\tcleLogger.init({\n\t\taccountSid: \"your-account-sid\"\n\t});\n\n\tcleLogger.registerTwilioActions(flex);\n}\n```\n\n**Note**: A Twilio account sid must be provided during ```init``` with the ```accountSid``` parameter, otherwise the actions won't be logged.\n\nThe logger will loop through all actions found under ```flex.Actions.actions```, and attach ```after``` event handlers to log the action name and payload. Any private properties found in the payload (starting with '_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Any actions names configured in ```excludedFlexActionNames``` will not be included in the logs.\n\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\n\n**Twilio Actions log levels**\n\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of ```INFO```.\n\n``` js\nAcceptTask: INFO\nCancelTransfer: INFO\nCompleteTask: INFO\nHangupCall: INFO\nHideDirectory: VERBOSE\nHistoryGo: VERBOSE\nHistoryGoBack: VERBOSE\nHistoryGoForward: VERBOSE\nHistoryPush: VERBOSE\nHistoryReplace: VERBOSE\nHoldCall: INFO\nHoldParticipant: INFO\nKickParticipant: INFO\nLogout: INFO\nMonitorCall: INFO\nNavigateToView: INFO\nRejectTask: INFO\nSelectTask: INFO\nSelectTaskInSupervisor: INFO\nSelectWorkerInSupervisor: INFO\nSendMessage: INFO\nSendTyping: VERBOSE\nSetActivity: INFO\nSetInputText: VERBOSE\nShowDirectory: VERBOSE\nStopMonitoringCall: INFO\nToggleMute: INFO\nToggleSidebar: VERBOSE\nTransferTask: INFO\nUnholdCall: INFO\nUnholdParticipant: INFO\nWrapupTask: INFO\n```\n\n### Twilio Event Hooks\n\nCurrently, Twilio only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with ```twilioEventHooks``` option, and use the following functions to send event data to a custom endpoint.\n\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is ```application/x-www-form-urlencoded``` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// unrelated properties omitted\n\ttwilioEventHooks: {\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\n\t}\n});\n\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\n```\n\nExample Azure Function hook for debugger events:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\nmodule.exports = async function (context, req) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\n\t\t}\n\t});\n\n\t// log to console\n\tcleLogger.info(\"Received Twilio debugger event\");\n\t\n\t// send event data to a configured url\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\n};\n```\n\n**NOTE:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to ```x-www-form-urlencoded``` format.\n\nExample Twilio function task router relay using the ```form-urlencoded``` npm package to convert the data to ```x-www-form-urlencoded``` format:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\nconst formurlencoded = require('form-urlencoded').default;\n\nexports.handler = async function(context, event, callback) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\n\t\t}\n\t});\n\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\n\n\tcallback();\n};\n```\n\n### Version\n\nVersion will be automatically included in all logs.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.version; // 1.0.0\n```\n\n## Log Object\n\n```js\n{\n\t// Account sid if provided during init\n\taccountSid: string;\n\n\t// Any additional log data to be included with the log\n\tadditionalData: unknown;\n\t\n\t// Component name, if provided\n\tcomponentName: string;\n\n\t// Generated or configured token\n\tcorrelationToken: string;\n\n\t// Current UTC datetime in ISO 8601 format\n\teventDate: string;\n\n\t// Log level\n\tlevel: string;\n\n\t// Log code, if provided\n\tlogCode: number;\n\n\t// Log message\n\tmessage: string;\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\tproperties: object;\n\n\t// BrowserFunction if the environment is the browser\n\t// CloudFunction if the environment is Node.js\n\tsource: string;\n\n\t// Logging library version \n\tloggerVersion: string;\n}\n```\n\n## Handling PII Data\n\nThe logger provides support for marking logs as containing PII (personally identifiable data), and configuring each log sink to indicate if it can handle PII data. By default, log sinks are not marked as PII safe, meaning they will not receive logs marked as containing PII. Marking a log sink as PII safe, means that the logger should be able to handle PII data without privacy violations (for example, a remote log sink that sends data to an API that masks PII data would be considered safe).\n\nAdditionally, the logger exports a function ```tagPII``` which can be used to tag parts of a string message as PII for easier masking. Tags can be used by backend systems to mask the data with a simple regex search and replace.\n\nExample tagged output:\n\n```js\nimport { tagPII } from \"@prftesp/cle-logger\";\n\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\n```\n\n## Advanced Configuration\n\n### Remote logging\n\nThe library provides a way to provide you own remote log url, or to remotely override the min log level. This can be useful for temporarily setting the log level to verbose in a production environment, for quick troubleshooting.\n\n**Remote log endpoint**\n\nTo implement your own remote logging endpoint, all you have to do is accept ```POST``` requests, with a JSON payload of an array of [log objects](#log-object), and return either ```{ success: true }``` or ```{ success: false }```. The endpoint should always return a ```200 OK``` status code. A good use-case would be leveraging a serverless api platform to output logs to a queue.\n\nExample Azure Function:\n\n```js\nmodule.exports = async function (context, req) {\n    context.log(req.body); // body will have the log array\n\n    context.res = {\n        status: 200,\n        body: { success: true }\n    };\n};\n```\n\n**Remote log level override**\n\nThe ```minLogLevelUrl``` configuration property is used to provide an endpoint that accepts ```GET``` requests and a ```accountSid``` query string parameter. ```accountSid``` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the ```minLogLevel``` configuration property. If null is returned, the ```minLogLevel``` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a ```200 OK``` status code and use the ```success``` property to indicate failure.\n\nExample success response:\n\n```json\n{ \n\t\"success\": true,\n\t\"minLogLevel\": \"warn\"\n}\n```\n\nExample error response:\n\n```json\n{ \n\t\"success\": false,\n\t\"minLogLevel\": null\n}\n```\n\n## Changelog\n\n### v2.0.0 [2019-12-20]\n\n**New features**\n- All logging output is now configured using the ```logSinks``` property. The library provides two commonly used loggers - ```consoleLogger``` and a ```remoteLogger``` that accepts the log url as a parameter. [Configuration](#configuration)\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\n- Log functions have a new optional ```containsPII``` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\n- A new helper function ```tagPII``` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\n\n**Breaking changes**\n- ```customLogSink``` configuration renamed to ```logSinks```\n- Removed ```disableRemoteLogging``` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\n- Removed ```logToConsole``` configuration. This is now configured as a log sink with a ```consoleLogger``` helper function.\n- Removed ```logUrl``` configuration. This is now configured as a log sink with a ```remoteLogger``` helper function.\n- Removed ```minLogLevel``` configuration. This is now configured per log sink.\n\n**Migration guide**\n``` javascript\nimport cleLogger from \"@prftesp/cle-logger\";\n\n// v1.0.0 and older config\ncleLogger.init({\n\tdisableRemoteLogSinks: true, // no direct replacement\n\tlogToConsole: true, // replaced by the \"console logger\" sink\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\n\tcustomLogSinks: [{\n\t\tname: \"custom sink\",\n\t\tlog: logs => {/* your custom logging */}\n\t}]\n});\n\n// v2.0.0 config\ncleLogger.init({\n\tlogSinks: [{\n\t\tname: \"custom sink\",\n\t\tlog: logs => {/* your custom logging */},\n\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t// set to true to get feature parity with older version,\n\t\t// please consider your use case before setting\n\t\tpiiSafe: true\n\t},\n\t{\n\t\tname: \"console logger\",\n\t\tlog: consoleLogger,\n\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t// set to true to get feature parity with older version,\n\t\t// please consider your use case before setting\n\t\tpiiSafe: true\n\t},\n\t{\n\t\tname: \"remote logger\",\n\t\tlog: remoteLogger(\"http://log.output/api\"),\n\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t// set to true to get feature parity with older version,\n\t\t// please consider your use case before setting\n\t\tpiiSafe: true\t\t\n\t}]\n});\n```\n\n### v1.0.0 [2019-09-12]\n\n- Initial release\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.0.3-beta.20200130.2","_nodeVersion":"12.14.1","_npmVersion":"6.13.6","dist":{"integrity":"sha512-rtl9KqbKx56rBrur1NOm0+SzvTcoxItCin/9bJkG3RzUOaBQLbffSzaJ+aXihw8tmV03mCKDVCpX/zghR+HEcQ==","shasum":"ccc3a6ea34c064d64ff4a72e7ac8488495cc9abd","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.0.3-beta.20200130.2.tgz","fileCount":17,"unpackedSize":61892,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeM1oPCRA9TVsSAnZWagAAuBkQAI9AKTLI2mF6WeHyjwSE\nv+K3KYjkNbAekIEHyXLpi8NbIdM0ZyUYnaUl3jOqZcRbBGmCUkkHBOmR4a4o\nqYDmket71XSXTgqTHwKkqpHd6GFv+ldpEmBBymSzvqJ52vqSq1MWRrxB0BhQ\n21w/ig5aaKg0AgTNBu6dJzkodGNFhyLIGgkLHvdqJqGcBj/uxpJUHlyoG4cU\nyb2QtiXPXDF2qU3K5279YZLZIlAm4STwDKS6bewcGj/ZaclfntlEn9iIPBlw\nm9lqtw2TfdOlhrYlWeKssxF/SIbqKQNPSV4zey6xo74sL6pdd2CDmXdyOkT+\nhgEZ+sMS3Ll1NWEI14CoRCE3g6ibQ2UcXmO3LwREotiqHqxpaO4MeW7xYdFQ\n6Qq0GdleTY4OtoEXKWRAtQaj6PivbvTpiPgyjbHnHmTcMmN0yj3A0OZ84vEu\n3lmfs+TlPNvtP2zCN05bMHpnEdWt0bRh/y9K+gf8o3IurfHYZLbMTr2QG18I\nWi2Ot+cB8yPpSeEhs1Pbcy+SwAJznxd3xjDNIZUMvOMiyvVxz6Q4AjVZFT/g\nR/cArozN/giWbk4v/P0Z8WmSFxAt8kzJ2xEMR2HnuJt6mabl66eiAYuf/p7z\nAMknMTKvZym//y2J7gso3EZKAocs4QkyLG2euvOeAcePSV+yb+ACmaXH52Hg\nnb72\r\n=r1Pw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDq5RmIoBi1Q0meZ9asvYYtTz7L3Y8N0V0GYrPyeN4x7gIgdcq852cu4P9fqnLb+E4TiPmLZMBzp21hHZ22eQep8FI="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.0.3-beta.20200130.2_1580423694699_0.7271052068708472"},"_hasShrinkwrap":false},"2.0.3":{"name":"@prftesp/cle-logger","version":"2.0.3","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@2.0.3","_nodeVersion":"12.14.1","_npmVersion":"6.13.6","dist":{"integrity":"sha512-aRpQeeSELlU78Mqw4Y1Z4yBlWh4F1OSWlPLl/bnwgYYN5o9At6rsZo/Rw0MAfNoOKMHa8xCQa0M34e92LetflQ==","shasum":"aa16368aa4fa8e952844f7cc01c952969b660bad","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.0.3.tgz","fileCount":17,"unpackedSize":61793,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeM1p9CRA9TVsSAnZWagAAD1oP/j2mAswA3hUOb/9UDObp\n+MgioFrwLFnhaNDdhFCqpT2Vp8vKr19494A2dBP/eDd3YEN9O/Q4mTzeGrhZ\nCjk4804YVQqpwIlUsn+/NMescMGMQgPfMWsto8eg6lSFpBW5KzyoNdWlfcLi\nhKndaf5J2NeZU2dR1hAKx9spL9B5EeVn/J76Dse3Hd5N4ly9COvVXs5exTnR\nDM804uA2x6jOKvwageM1XH1hwp6kUJQeh2j8Bpll/5Yft4oH9hY3iWmWpBkX\nFwnFEPbrU9LmeEWvbGvs65yO1vP/0lLJyQsiKjMCDqOpMB+HM+QdMYDA6TJw\nC1orH1UTyOCt2CmnRu3zr+W8Ibc8inNbJUnqsdt4Ogx2R263cs3rS8JWjiX2\n0+Us98iSWcVslCyGVOkurRziw7B70XBYNP6dh8PHi7XmXDbKLZSstubraPZc\nlqg1YLn05HX6Tn4WVbjAsfZlEEra6YZV9d2cTghQEVic3RKKyJp3QISm4/UZ\n23Bw7ulE+bAK4Fsr6V/QiYp6TxO3uzzdbbTd0Nev1Yr7XOG7YEeC0Rr0aANB\nm8L6wYTlIonEOhI479ZQSreZbXhz4V1pVZ3kUa5WGPMrSA+kcfiADL6c3VCs\nsCTFdh1SXkiU0ZaFxK2Gge9tSHwNv6nKsvhJGbX4YWhZyj8nONLWW7fSyR8U\ncjxh\r\n=a0gS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDEus+aU6R/FqX0ZJ9XoXgDvDK4rZ3HAMaVAK4lmHcCvAiEAq/x/XBn/4yMfTxvU4FiiBNQs5hcx2rPUqg4JiTDXDPg="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.0.3_1580423804826_0.5798655377246928"},"_hasShrinkwrap":false},"2.0.4-beta.0":{"name":"@prftesp/cle-logger","version":"2.0.4-beta.0","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿CLE Logger\r\n===\r\n\r\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\r\n\r\n## Documentation\r\n\r\n- [Target Environment](#target-environment)\r\n- [Simple Example](#simple-example)\r\n- [Installation](#installation)\r\n- [Features](#features)\r\n- [Configuration](#configuration)\r\n- [API](#api)\r\n\t- [Log sinks](#log-sinks)\r\n\t- [Logging levels](#logging-levels)\r\n\t- [Logging parameters](#logging-parameters)\r\n\t- [Available component names](#available-component-names)\r\n\t- [Available custom property keys](#available-custom-property-keys)\r\n\t- [Log codes](#log-codes)\r\n\t- [Registering Twilio Actions](#registering-twilio-actions)\r\n\t- [Twilio Event Hooks](#twilio-event-hooks)\r\n\t- [Version](#version)\r\n- [Log Object](#log-object)\r\n- [Handling PII Data](#handling-pii-data)\r\n- [Advanced Configuration](#advanced-configuration)\r\n\t- [Remote logging](#remote-logging)\r\n- [Changelog](#changelog)\r\n\r\n## Target Environment\r\n\r\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\r\n\r\n## Simple Example\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"console sink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\n// will log message to console\r\ncleLogger.info(\"log message\");\r\n```\r\n\r\n## Installation\r\n\r\n```bash\r\n$ npm install @prftesp/cle-logger\r\n```\r\n\r\n## Features\r\n\r\n- Runs in Node.js and the browser\r\n- Remote logging\r\n- Multiple log sinks\r\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\r\n- Remote minimum log level overrides\r\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\r\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\r\n- Support for tagging logs with custom component names or correlation tokens\r\n- Send Twilio event hook data to a custom backend\r\n\r\n## Configuration\r\n\r\nTo configure the logger call the ```init``` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\r\n\r\n**Available options**\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// Twilio account sid, required for use with CLE\r\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\r\n\r\n\t// Will be associated with all logs,\r\n\t// if not provided, a unique uuid will be generated\r\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\r\n\t// An array of log sinks that will receive an array of log objects,\r\n\t// and can optionally return a promise\r\n\tlogSinks: [{\r\n\t\t// name of the log sink\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\t// log handler function\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\t// should this log sink accept PII data, defaults to false\r\n\t\tpiiSafe: true,\r\n\t\t// log level for the sink, defaults to INFO\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n\r\n\t// A way to suppress error messages if set to false, defaults to true\r\n\tlogErrorsToConsole: true,\r\n\r\n\t// If logging Twilio Flex Actions, provide a list of actions \r\n\t// to be excluded from logs\r\n\texcludedFlexActionNames: [\"SendTyping\"],\r\n\r\n\t// A custom endpoint to fetch log level overrides\r\n\t// NOTE: the provided url should always end with 'accountSid=',\r\n\t// the library will append the accountSid\r\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\r\n\r\n\t// An instance of ('@twilio/flex-ui').Manager\r\n\ttwilioFlexManager: manager,\r\n\r\n\t// To send data from Twilio event hooks, to a custom backend,\r\n\t// configure one or more endpoints for each event type\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\r\n\t}\r\n});\r\n```\r\n\r\n## API\r\n\r\nThe logger can be imported as an object in CommonJs or ES6 syntax:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n// or\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n```\r\n\r\nAlternatively, all top level members of the ```cleLogger``` are available as named exports and can be imported separately:\r\n\r\n```js\r\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tlogSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\r\n\r\n// vs\r\n\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tcleLogger.logSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\r\n```\r\n\r\n### Log sinks\r\n\r\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the ```logSinks``` config. It's an array of objects containing a ```name```, ```piiSafe``` flag to indicate if the sink can handle PII data without concern for privacy violations, ```logLevel```, and a ```log``` method. The ```log``` method will receive an array of log objects for you to handle. All provided log sinks will be executed in parallel. Log sinks should be async operations, and must return a Promise object. Built-in logger already do this, if you have a custom logger, make sure to return a promise.\r\n\r\nTwo built-in loggers are available out of the box. ```consoleLogger``` for logging to standard console, and ```remoteLogger``` for sending logs over http(s).\r\n\r\nLog sink object:\r\n\r\n``` js\r\n{\r\n\t// name of the log sink\r\n\tname: \"exampleConsoleSink\",\r\n\t// log handler function\r\n\tlog: logs => { \r\n\t\tconsole.log(logs);\r\n\t\treturn Promise.resolve();\r\n\t},\r\n\t// should this log sink accept PII data, defaults to false\r\n\tpiiSafe: true,\r\n\t// log level for the sink, defaults to INFO\r\n\tlogLevel: cleLogger.logLevels.VERBOSE\r\n}\r\n```\r\n\r\nExample log sinks:\r\n\r\n``` js\r\nconst fs = require(\"fs\");\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"fileSink\",\r\n\t\tlog: logs => {\r\n\t\t\tfs.appendFile(\r\n\t\t\t\t\"./log.txt\",\r\n\t\t\t\t`${JSON.stringify(logs)}`,\r\n\t\t\t\tconsole.log\r\n\t\t\t);\r\n\t\t\treturn Promise.resolve();\r\n\t\t}\r\n\t},\r\n\t{\r\n\t\tname: \"remoteSink\",\r\n\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\r\n\t\tlogLevel: cleLogger.logLevels.WARN,\r\n\t\tpiiSafe: true\r\n\t},\r\n\t{\r\n\t\tname: \"consoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}]\r\n});\r\n```\r\n\r\n### Logging levels\r\n\r\nLogging level constants can be found in ```cleLogger.logLevels```.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.logLevels.VERBOSE; // verbose\r\ncleLogger.logLevels.INFO; // info\r\ncleLogger.logLevels.WARN; // warn\r\ncleLogger.logLevels.ERROR; // error\r\ncleLogger.logLevels.CRITICAL; // critical\r\n```\r\n\r\nThere are functions available for each supported logging level:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(...);\r\ncleLogger.info(...);\r\ncleLogger.warn(...);\r\ncleLogger.error(...);\r\ncleLogger.critical(...);\r\n```\r\n\r\nThe functions are all async, return ```Promise<void>```, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\r\n\r\n### Logging parameters\r\n\r\nAll logging functions take the same parameters:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(\r\n\t// The only required property, no logs will be generated without it\r\n\t\"Log message\",\r\n\r\n\t// Any additional log data you would like to include with the log,\r\n\t// can be any data type\r\n\t{ data: \"test\" },\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\t// CLE uses it to index these properties in Application Insights\r\n\t// For CLE, try to use keys found in 'cleLogger.customPropertyKeys'\r\n\t{ userId: 123 },\r\n\r\n\t// Another useful way to 'tag' your logs for easier searching\r\n\t// Can be any string, but for CLE, try to use available options in 'cleLogger.componentNames'\r\n\t// If you would like more component names added, please let us know \r\n\t\"ProfileComponent\",\r\n\r\n\t// Log code, can be any number used to categorize logs,\r\n\t// there will be a separate document listing CLE codes\r\n\t70123,\r\n\r\n\t// Correlation token to be associated with this log only,\r\n\t// if one is not provided, the token supplied/generated during 'init' will be used\r\n\t\"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\t\r\n\t// Does the log contain PII data, defaults to false. If set to true, the log will be \r\n\t// passed in only to those log sinks that have 'piiSafe' set to true.\r\n\ttrue,\r\n);\r\n```\r\n\r\n### Available component names\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.componentNames.SESSION;\r\ncleLogger.componentNames.DIALER;\r\ncleLogger.componentNames.DASHBOARD;\r\ncleLogger.componentNames.AFTERCALL;\r\ncleLogger.componentNames.SUPERVISOR;\r\n```\r\n\r\n### Available custom property keys\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.customPropertyKeys.CHANNEL_SID;\r\ncleLogger.customPropertyKeys.TASK_SID;\r\ncleLogger.customPropertyKeys.CALL_SID;\r\ncleLogger.customPropertyKeys.WORKFLOW_SID;\r\ncleLogger.customPropertyKeys.WORKER_SID;\r\n```\r\n\r\n### Log codes\r\n\r\nTo categorize a log in one of the following categories, provide a log code in the appropriate range:\r\n\r\n- 100 000 - 109 999 => CALL_STATUS\r\n- 110 000 - 119 999 => IVR\r\n- 120 000 - 129 999 => ROUTING\r\n- 130 000 - 139 999 => AGENT_ACTION\r\n- 140 000 - 149 999 => TRANSFER\r\n- 150 000 - 159 999 => RM_INTEGRATION\r\n- 160 000 - 169 999 => WFM_INTEGRATION\r\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\r\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\r\n\r\n### Registering Twilio Actions\r\n\r\nExample in a Twilio Flex UI plugin:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\n/**\r\n * @param flex { typeof import('@twilio/flex-ui') }\r\n * @param manager { import('@twilio/flex-ui').Manager }\r\n */\r\ninit(flex, manager) {\r\n\tcleLogger.init({\r\n\t\taccountSid: \"your-account-sid\"\r\n\t});\r\n\r\n\tcleLogger.registerTwilioActions(flex);\r\n}\r\n```\r\n\r\n**Note**: A Twilio account sid must be provided during ```init``` with the ```accountSid``` parameter, otherwise the actions won't be logged.\r\n\r\nThe logger will loop through all actions found under ```flex.Actions.actions```, and attach ```after``` event handlers to log the action name and payload. Any private properties found in the payload (starting with '_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Any actions names configured in ```excludedFlexActionNames``` will not be included in the logs.\r\n\r\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\r\n\r\n**Twilio Actions log levels**\r\n\r\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of ```INFO```.\r\n\r\n``` js\r\nAcceptTask: INFO\r\nCancelTransfer: INFO\r\nCompleteTask: INFO\r\nHangupCall: INFO\r\nHideDirectory: VERBOSE\r\nHistoryGo: VERBOSE\r\nHistoryGoBack: VERBOSE\r\nHistoryGoForward: VERBOSE\r\nHistoryPush: VERBOSE\r\nHistoryReplace: VERBOSE\r\nHoldCall: INFO\r\nHoldParticipant: INFO\r\nKickParticipant: INFO\r\nLogout: INFO\r\nMonitorCall: INFO\r\nNavigateToView: INFO\r\nRejectTask: INFO\r\nSelectTask: INFO\r\nSelectTaskInSupervisor: INFO\r\nSelectWorkerInSupervisor: INFO\r\nSendMessage: INFO\r\nSendTyping: VERBOSE\r\nSetActivity: INFO\r\nSetInputText: VERBOSE\r\nShowDirectory: VERBOSE\r\nStopMonitoringCall: INFO\r\nToggleMute: INFO\r\nToggleSidebar: VERBOSE\r\nTransferTask: INFO\r\nUnholdCall: INFO\r\nUnholdParticipant: INFO\r\nWrapupTask: INFO\r\n```\r\n\r\n### Twilio Event Hooks\r\n\r\nCurrently, Twilio only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with ```twilioEventHooks``` option, and use the following functions to send event data to a custom endpoint.\r\n\r\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is ```application/x-www-form-urlencoded``` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// unrelated properties omitted\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\r\n\t}\r\n});\r\n\r\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\r\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\r\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\r\n```\r\n\r\nExample Azure Function hook for debugger events:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\nmodule.exports = async function (context, req) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\t}\r\n\t});\r\n\r\n\t// log to console\r\n\tcleLogger.info(\"Received Twilio debugger event\");\r\n\t\r\n\t// send event data to a configured url\r\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\r\n};\r\n```\r\n\r\n**NOTE:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to ```x-www-form-urlencoded``` format.\r\n\r\nExample Twilio function task router relay using the ```form-urlencoded``` npm package to convert the data to ```x-www-form-urlencoded``` format:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\nconst formurlencoded = require('form-urlencoded').default;\r\n\r\nexports.handler = async function(context, event, callback) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\t}\r\n\t});\r\n\r\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\r\n\r\n\tcallback();\r\n};\r\n```\r\n\r\n### Version\r\n\r\nVersion will be automatically included in all logs.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.version; // 1.0.0\r\n```\r\n\r\n## Log Object\r\n\r\n```js\r\n{\r\n\t// Account sid if provided during init\r\n\taccountSid: string;\r\n\r\n\t// Any additional log data to be included with the log\r\n\tadditionalData: unknown;\r\n\t\r\n\t// Component name, if provided\r\n\tcomponentName: string;\r\n\r\n\t// Generated or configured token\r\n\tcorrelationToken: string;\r\n\r\n\t// Current UTC datetime in ISO 8601 format\r\n\teventDate: string;\r\n\r\n\t// Log level\r\n\tlevel: string;\r\n\r\n\t// Log code, if provided\r\n\tlogCode: number;\r\n\r\n\t// Log message\r\n\tmessage: string;\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\tproperties: object;\r\n\r\n\t// BrowserFunction if the environment is the browser\r\n\t// CloudFunction if the environment is Node.js\r\n\tsource: string;\r\n\r\n\t// Logging library version \r\n\tloggerVersion: string;\r\n}\r\n```\r\n\r\n## Handling PII Data\r\n\r\nThe logger provides support for marking logs as containing PII (personally identifiable data), and configuring each log sink to indicate if it can handle PII data. By default, log sinks are not marked as PII safe, meaning they will not receive logs marked as containing PII. Marking a log sink as PII safe, means that the logger should be able to handle PII data without privacy violations (for example, a remote log sink that sends data to an API that masks PII data would be considered safe).\r\n\r\nAdditionally, the logger exports a function ```tagPII``` which can be used to tag parts of a string message as PII for easier masking. Tags can be used by backend systems to mask the data with a simple regex search and replace.\r\n\r\nExample tagged output:\r\n\r\n```js\r\nimport { tagPII } from \"@prftesp/cle-logger\";\r\n\r\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\r\n```\r\n\r\n## Advanced Configuration\r\n\r\n### Remote logging\r\n\r\nThe library provides a way to provide you own remote log url, or to remotely override the min log level. This can be useful for temporarily setting the log level to verbose in a production environment, for quick troubleshooting.\r\n\r\n**Remote log endpoint**\r\n\r\nTo implement your own remote logging endpoint, all you have to do is accept ```POST``` requests, with a JSON payload of an array of [log objects](#log-object), and return either ```{ success: true }``` or ```{ success: false }```. The endpoint should always return a ```200 OK``` status code. A good use-case would be leveraging a serverless api platform to output logs to a queue.\r\n\r\nExample Azure Function:\r\n\r\n```js\r\nmodule.exports = async function (context, req) {\r\n    context.log(req.body); // body will have the log array\r\n\r\n    context.res = {\r\n        status: 200,\r\n        body: { success: true }\r\n    };\r\n};\r\n```\r\n\r\n**Remote log level override**\r\n\r\nThe ```minLogLevelUrl``` configuration property is used to provide an endpoint that accepts ```GET``` requests and a ```accountSid``` query string parameter. ```accountSid``` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the ```minLogLevel``` configuration property. If null is returned, the ```minLogLevel``` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a ```200 OK``` status code and use the ```success``` property to indicate failure.\r\n\r\nExample success response:\r\n\r\n```json\r\n{ \r\n\t\"success\": true,\r\n\t\"minLogLevel\": \"warn\"\r\n}\r\n```\r\n\r\nExample error response:\r\n\r\n```json\r\n{ \r\n\t\"success\": false,\r\n\t\"minLogLevel\": null\r\n}\r\n```\r\n\r\n## Changelog\r\n\r\n### v2.0.0 [2019-12-20]\r\n\r\n**New features**\r\n- All logging output is now configured using the ```logSinks``` property. The library provides two commonly used loggers - ```consoleLogger``` and a ```remoteLogger``` that accepts the log url as a parameter. [Configuration](#configuration)\r\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\r\n- Log functions have a new optional ```containsPII``` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\r\n- A new helper function ```tagPII``` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\r\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\r\n\r\n**Breaking changes**\r\n- ```customLogSink``` configuration renamed to ```logSinks```\r\n- Removed ```disableRemoteLogging``` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\r\n- Removed ```logToConsole``` configuration. This is now configured as a log sink with a ```consoleLogger``` helper function.\r\n- Removed ```logUrl``` configuration. This is now configured as a log sink with a ```remoteLogger``` helper function.\r\n- Removed ```minLogLevel``` configuration. This is now configured per log sink.\r\n\r\n**Migration guide**\r\n``` javascript\r\nimport cleLogger from \"@prftesp/cle-logger\";\r\n\r\n// v1.0.0 and older config\r\ncleLogger.init({\r\n\tdisableRemoteLogSinks: true, // no direct replacement\r\n\tlogToConsole: true, // replaced by the \"console logger\" sink\r\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\r\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\r\n\tcustomLogSinks: [{\r\n\t\tname: \"custom sink\",\r\n\t\tlog: logs => {/* your custom logging */}\r\n\t}]\r\n});\r\n\r\n// v2.0.0 config\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"custom sink\",\r\n\t\tlog: logs => {/* your custom logging */},\r\n\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t// set to true to get feature parity with older version,\r\n\t\t// please consider your use case before setting\r\n\t\tpiiSafe: true\r\n\t},\r\n\t{\r\n\t\tname: \"console logger\",\r\n\t\tlog: consoleLogger,\r\n\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t// set to true to get feature parity with older version,\r\n\t\t// please consider your use case before setting\r\n\t\tpiiSafe: true\r\n\t},\r\n\t{\r\n\t\tname: \"remote logger\",\r\n\t\tlog: remoteLogger(\"http://log.output/api\"),\r\n\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t// set to true to get feature parity with older version,\r\n\t\t// please consider your use case before setting\r\n\t\tpiiSafe: true\t\t\r\n\t}]\r\n});\r\n```\r\n\r\n### v1.0.0 [2019-09-12]\r\n\r\n- Initial release\r\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.0.4-beta.0","_nodeVersion":"10.15.2","_npmVersion":"6.11.3","dist":{"integrity":"sha512-KcTgf+3EYyjCVo9O7ktUO9wgdz/mTm0VRj6320j9nbkDnAEJPemcf/hJm5TnRwaABeyvi+HV1c36vwHFD8lZZQ==","shasum":"edb029be2f6256683f1b5ab22464a062212c43ab","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.0.4-beta.0.tgz","fileCount":17,"unpackedSize":67274,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeOJRlCRA9TVsSAnZWagAAlvcP/3BtH/0SC3ux0MvOX4Qb\nrmY5n1SmGsLU4Z2/qaGrzk3cXSs0VikkKZ1tCCTu1QxxlxqZqgcRBR1NK9Ts\nvHRQAqT0sczHgKiLnJKHB5x1fWo+2CSaWDioaYqRdBaghHTsq/g/wGa19MES\nNL8SozNviv8YetaUvRhIS0xh0JJG88CBO2Gy+oJ54DQDYhuASo6JcCaDH6Ai\nXcQ2JhijCLZOAMbjGCrCAHJ/0gLgoKQzyzVnHGGiGw3/Tu9V6+r8EvEM9Bdj\nz5QrArQJ4yRC/fO3E97ZMIQi0gvZy2+gpmDK0bvrrWDPq6D68FOlME+H98Zd\nhmi/eUlzW3TOPIQDm8p5fQ90DE6T6pCJPZyborSiv3wV8ktFwZwp8NG9fM21\nadUukI+lUKDtAH+EBzI6Qysa28eMmNZlvfqzYrew+dbsAYxW0bPc3lRKyKqg\nsV/W9snl68BXxxM5CEU5LSP7P3NvOMUDV7HVuK9AQX71IfEDwtLC05bSv7IN\n6zxrd1r9hpMc2qpxQCiOK/FvQT+GP3dVwa1sG69wPyzGbdsapes/yQVDyUCS\nSCsz7WD0CS/njLKN0xdDFCLISQIo0RhhRweYV9w5MAhpN5SGzDyA23AnBd74\nELcq0l6XYTwrMVt8DNwRe+vIUl8t/Mr4EG0BBCn2CtSd9QZ23YXVDOHeEjyj\ncJZJ\r\n=Uz0K\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDEbkN3jdCJ+7kplQqy/Bfm4Ir+Wzephlr/E8N05QrHagIgMDtJi3olNKwO3TAQ7Fg6R60Z0BErA6EL+NbYpONw9JM="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.0.4-beta.0_1580766308460_0.07117097406159556"},"_hasShrinkwrap":false},"2.0.4-beta.1":{"name":"@prftesp/cle-logger","version":"2.0.4-beta.1","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿CLE Logger\r\n===\r\n\r\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\r\n\r\n## Documentation\r\n\r\n- [Target Environment](#target-environment)\r\n- [Simple Example](#simple-example)\r\n- [Installation](#installation)\r\n- [Features](#features)\r\n- [Configuration](#configuration)\r\n- [API](#api)\r\n\t- [Log sinks](#log-sinks)\r\n\t- [Logging levels](#logging-levels)\r\n\t- [Logging parameters](#logging-parameters)\r\n\t- [Available component names](#available-component-names)\r\n\t- [Available custom property keys](#available-custom-property-keys)\r\n\t- [Log codes](#log-codes)\r\n\t- [Registering Twilio Actions](#registering-twilio-actions)\r\n\t- [Twilio Event Hooks](#twilio-event-hooks)\r\n\t- [Version](#version)\r\n- [Log Object](#log-object)\r\n- [Handling PII Data](#handling-pii-data)\r\n- [Advanced Configuration](#advanced-configuration)\r\n\t- [Remote logging](#remote-logging)\r\n- [Changelog](#changelog)\r\n\r\n## Target Environment\r\n\r\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\r\n\r\n## Simple Example\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"console sink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\n// will log message to console\r\ncleLogger.info(\"log message\");\r\n```\r\n\r\n## Installation\r\n\r\n```bash\r\n$ npm install @prftesp/cle-logger\r\n```\r\n\r\n## Features\r\n\r\n- Runs in Node.js and the browser\r\n- Remote logging\r\n- Multiple log sinks\r\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\r\n- Remote minimum log level overrides\r\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\r\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\r\n- Support for tagging logs with custom component names or correlation tokens\r\n- Send Twilio event hook data to a custom backend\r\n\r\n## Configuration\r\n\r\nTo configure the logger call the ```init``` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\r\n\r\n**Available options**\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// Twilio account sid, required for use with CLE\r\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\r\n\r\n\t// Will be associated with all logs,\r\n\t// if not provided, a unique uuid will be generated\r\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\r\n\t// An array of log sinks that will receive an array of log objects,\r\n\t// and can optionally return a promise\r\n\tlogSinks: [{\r\n\t\t// name of the log sink\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\t// log handler function\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\t// should this log sink accept PII data, defaults to false\r\n\t\tpiiSafe: true,\r\n\t\t// log level for the sink, defaults to INFO\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n\r\n\t// A way to suppress error messages if set to false, defaults to true\r\n\tlogErrorsToConsole: true,\r\n\r\n\t// If logging Twilio Flex Actions, provide a list of actions \r\n\t// to be excluded from logs\r\n\texcludedFlexActionNames: [\"SendTyping\"],\r\n\r\n\t// A custom endpoint to fetch log level overrides\r\n\t// NOTE: the provided url should always end with 'accountSid=',\r\n\t// the library will append the accountSid\r\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\r\n\r\n\t// An instance of ('@twilio/flex-ui').Manager\r\n\ttwilioFlexManager: manager,\r\n\r\n\t// To send data from Twilio event hooks, to a custom backend,\r\n\t// configure one or more endpoints for each event type\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\r\n\t}\r\n});\r\n```\r\n\r\n## API\r\n\r\nThe logger can be imported as an object in CommonJs or ES6 syntax:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n// or\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n```\r\n\r\nAlternatively, all top level members of the ```cleLogger``` are available as named exports and can be imported separately:\r\n\r\n```js\r\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tlogSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\r\n\r\n// vs\r\n\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tcleLogger.logSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\r\n```\r\n\r\n### Log sinks\r\n\r\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the ```logSinks``` config. It's an array of objects containing a ```name```, ```piiSafe``` flag to indicate if the sink can handle PII data without concern for privacy violations, ```logLevel```, and a ```log``` method. The ```log``` method will receive an array of log objects for you to handle. All provided log sinks will be executed in parallel. Log sinks should be async operations, and must return a Promise object. Built-in logger already do this, if you have a custom logger, make sure to return a promise.\r\n\r\nTwo built-in loggers are available out of the box. ```consoleLogger``` for logging to standard console, and ```remoteLogger``` for sending logs over http(s).\r\n\r\nLog sink object:\r\n\r\n``` js\r\n{\r\n\t// name of the log sink\r\n\tname: \"exampleConsoleSink\",\r\n\t// log handler function\r\n\tlog: logs => { \r\n\t\tconsole.log(logs);\r\n\t\treturn Promise.resolve();\r\n\t},\r\n\t// should this log sink accept PII data, defaults to false\r\n\tpiiSafe: true,\r\n\t// log level for the sink, defaults to INFO\r\n\tlogLevel: cleLogger.logLevels.VERBOSE\r\n}\r\n```\r\n\r\nExample log sinks:\r\n\r\n``` js\r\nconst fs = require(\"fs\");\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"fileSink\",\r\n\t\tlog: logs => {\r\n\t\t\tfs.appendFile(\r\n\t\t\t\t\"./log.txt\",\r\n\t\t\t\t`${JSON.stringify(logs)}`,\r\n\t\t\t\tconsole.log\r\n\t\t\t);\r\n\t\t\treturn Promise.resolve();\r\n\t\t}\r\n\t},\r\n\t{\r\n\t\tname: \"remoteSink\",\r\n\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\r\n\t\tlogLevel: cleLogger.logLevels.WARN,\r\n\t\tpiiSafe: true\r\n\t},\r\n\t{\r\n\t\tname: \"consoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}]\r\n});\r\n```\r\n\r\n### Logging levels\r\n\r\nLogging level constants can be found in ```cleLogger.logLevels```.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.logLevels.VERBOSE; // verbose\r\ncleLogger.logLevels.INFO; // info\r\ncleLogger.logLevels.WARN; // warn\r\ncleLogger.logLevels.ERROR; // error\r\ncleLogger.logLevels.CRITICAL; // critical\r\n```\r\n\r\nThere are functions available for each supported logging level:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(...);\r\ncleLogger.info(...);\r\ncleLogger.warn(...);\r\ncleLogger.error(...);\r\ncleLogger.critical(...);\r\n```\r\n\r\nThe functions are all async, return ```Promise<void>```, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\r\n\r\n### Logging parameters\r\n\r\nAll logging functions take the same parameters:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(\r\n\t// The only required property, no logs will be generated without it\r\n\t\"Log message\",\r\n\r\n\t// Any additional log data you would like to include with the log,\r\n\t// can be any data type\r\n\t{ data: \"test\" },\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\t// CLE uses it to index these properties in Application Insights\r\n\t// For CLE, try to use keys found in 'cleLogger.customPropertyKeys'\r\n\t{ userId: 123 },\r\n\r\n\t// Another useful way to 'tag' your logs for easier searching\r\n\t// Can be any string, but for CLE, try to use available options in 'cleLogger.componentNames'\r\n\t// If you would like more component names added, please let us know \r\n\t\"ProfileComponent\",\r\n\r\n\t// Log code, can be any number used to categorize logs,\r\n\t// there will be a separate document listing CLE codes\r\n\t70123,\r\n\r\n\t// Correlation token to be associated with this log only,\r\n\t// if one is not provided, the token supplied/generated during 'init' will be used\r\n\t\"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\t\r\n\t// Does the log contain PII data, defaults to false. If set to true, the log will be \r\n\t// passed in only to those log sinks that have 'piiSafe' set to true.\r\n\ttrue,\r\n);\r\n```\r\n\r\n### Available component names\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.componentNames.SESSION;\r\ncleLogger.componentNames.DIALER;\r\ncleLogger.componentNames.DASHBOARD;\r\ncleLogger.componentNames.AFTERCALL;\r\ncleLogger.componentNames.SUPERVISOR;\r\n```\r\n\r\n### Available custom property keys\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.customPropertyKeys.CHANNEL_SID;\r\ncleLogger.customPropertyKeys.TASK_SID;\r\ncleLogger.customPropertyKeys.CALL_SID;\r\ncleLogger.customPropertyKeys.WORKFLOW_SID;\r\ncleLogger.customPropertyKeys.WORKER_SID;\r\n```\r\n\r\n### Log codes\r\n\r\nTo categorize a log in one of the following categories, provide a log code in the appropriate range:\r\n\r\n- 100 000 - 109 999 => CALL_STATUS\r\n- 110 000 - 119 999 => IVR\r\n- 120 000 - 129 999 => ROUTING\r\n- 130 000 - 139 999 => AGENT_ACTION\r\n- 140 000 - 149 999 => TRANSFER\r\n- 150 000 - 159 999 => RM_INTEGRATION\r\n- 160 000 - 169 999 => WFM_INTEGRATION\r\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\r\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\r\n\r\n### Registering Twilio Actions\r\n\r\nExample in a Twilio Flex UI plugin:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\n/**\r\n * @param flex { typeof import('@twilio/flex-ui') }\r\n * @param manager { import('@twilio/flex-ui').Manager }\r\n */\r\ninit(flex, manager) {\r\n\tcleLogger.init({\r\n\t\taccountSid: \"your-account-sid\"\r\n\t});\r\n\r\n\tcleLogger.registerTwilioActions(flex);\r\n}\r\n```\r\n\r\n**Note**: A Twilio account sid must be provided during ```init``` with the ```accountSid``` parameter, otherwise the actions won't be logged.\r\n\r\nThe logger will loop through all actions found under ```flex.Actions.actions```, and attach ```after``` event handlers to log the action name and payload. Any private properties found in the payload (starting with '_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Any actions names configured in ```excludedFlexActionNames``` will not be included in the logs.\r\n\r\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\r\n\r\n**Twilio Actions log levels**\r\n\r\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of ```INFO```.\r\n\r\n``` js\r\nAcceptTask: INFO\r\nCancelTransfer: INFO\r\nCompleteTask: INFO\r\nHangupCall: INFO\r\nHideDirectory: VERBOSE\r\nHistoryGo: VERBOSE\r\nHistoryGoBack: VERBOSE\r\nHistoryGoForward: VERBOSE\r\nHistoryPush: VERBOSE\r\nHistoryReplace: VERBOSE\r\nHoldCall: INFO\r\nHoldParticipant: INFO\r\nKickParticipant: INFO\r\nLogout: INFO\r\nMonitorCall: INFO\r\nNavigateToView: INFO\r\nRejectTask: INFO\r\nSelectTask: INFO\r\nSelectTaskInSupervisor: INFO\r\nSelectWorkerInSupervisor: INFO\r\nSendMessage: INFO\r\nSendTyping: VERBOSE\r\nSetActivity: INFO\r\nSetInputText: VERBOSE\r\nShowDirectory: VERBOSE\r\nStopMonitoringCall: INFO\r\nToggleMute: INFO\r\nToggleSidebar: VERBOSE\r\nTransferTask: INFO\r\nUnholdCall: INFO\r\nUnholdParticipant: INFO\r\nWrapupTask: INFO\r\n```\r\n\r\n### Twilio Event Hooks\r\n\r\nCurrently, Twilio only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with ```twilioEventHooks``` option, and use the following functions to send event data to a custom endpoint.\r\n\r\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is ```application/x-www-form-urlencoded``` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// unrelated properties omitted\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\r\n\t}\r\n});\r\n\r\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\r\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\r\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\r\n```\r\n\r\nExample Azure Function hook for debugger events:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\nmodule.exports = async function (context, req) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\t}\r\n\t});\r\n\r\n\t// log to console\r\n\tcleLogger.info(\"Received Twilio debugger event\");\r\n\t\r\n\t// send event data to a configured url\r\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\r\n};\r\n```\r\n\r\n**NOTE:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to ```x-www-form-urlencoded``` format.\r\n\r\nExample Twilio function task router relay using the ```form-urlencoded``` npm package to convert the data to ```x-www-form-urlencoded``` format:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\nconst formurlencoded = require('form-urlencoded').default;\r\n\r\nexports.handler = async function(context, event, callback) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\t}\r\n\t});\r\n\r\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\r\n\r\n\tcallback();\r\n};\r\n```\r\n\r\n### Version\r\n\r\nVersion will be automatically included in all logs.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.version; // 1.0.0\r\n```\r\n\r\n## Log Object\r\n\r\n```js\r\n{\r\n\t// Account sid if provided during init\r\n\taccountSid: string;\r\n\r\n\t// Any additional log data to be included with the log\r\n\tadditionalData: unknown;\r\n\t\r\n\t// Component name, if provided\r\n\tcomponentName: string;\r\n\r\n\t// Generated or configured token\r\n\tcorrelationToken: string;\r\n\r\n\t// Current UTC datetime in ISO 8601 format\r\n\teventDate: string;\r\n\r\n\t// Log level\r\n\tlevel: string;\r\n\r\n\t// Log code, if provided\r\n\tlogCode: number;\r\n\r\n\t// Log message\r\n\tmessage: string;\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\tproperties: object;\r\n\r\n\t// BrowserFunction if the environment is the browser\r\n\t// CloudFunction if the environment is Node.js\r\n\tsource: string;\r\n\r\n\t// Logging library version \r\n\tloggerVersion: string;\r\n}\r\n```\r\n\r\n## Handling PII Data\r\n\r\nThe logger provides support for marking logs as containing PII (personally identifiable data), and configuring each log sink to indicate if it can handle PII data. By default, log sinks are not marked as PII safe, meaning they will not receive logs marked as containing PII. Marking a log sink as PII safe, means that the logger should be able to handle PII data without privacy violations (for example, a remote log sink that sends data to an API that masks PII data would be considered safe).\r\n\r\nAdditionally, the logger exports a function ```tagPII``` which can be used to tag parts of a string message as PII for easier masking. Tags can be used by backend systems to mask the data with a simple regex search and replace.\r\n\r\nExample tagged output:\r\n\r\n```js\r\nimport { tagPII } from \"@prftesp/cle-logger\";\r\n\r\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\r\n```\r\n\r\n## Advanced Configuration\r\n\r\n### Remote logging\r\n\r\nThe library provides a way to provide you own remote log url, or to remotely override the min log level. This can be useful for temporarily setting the log level to verbose in a production environment, for quick troubleshooting.\r\n\r\n**Remote log endpoint**\r\n\r\nTo implement your own remote logging endpoint, all you have to do is accept ```POST``` requests, with a JSON payload of an array of [log objects](#log-object), and return either ```{ success: true }``` or ```{ success: false }```. The endpoint should always return a ```200 OK``` status code. A good use-case would be leveraging a serverless api platform to output logs to a queue.\r\n\r\nExample Azure Function:\r\n\r\n```js\r\nmodule.exports = async function (context, req) {\r\n    context.log(req.body); // body will have the log array\r\n\r\n    context.res = {\r\n        status: 200,\r\n        body: { success: true }\r\n    };\r\n};\r\n```\r\n\r\n**Remote log level override**\r\n\r\nThe ```minLogLevelUrl``` configuration property is used to provide an endpoint that accepts ```GET``` requests and a ```accountSid``` query string parameter. ```accountSid``` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the ```minLogLevel``` configuration property. If null is returned, the ```minLogLevel``` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a ```200 OK``` status code and use the ```success``` property to indicate failure.\r\n\r\nExample success response:\r\n\r\n```json\r\n{ \r\n\t\"success\": true,\r\n\t\"minLogLevel\": \"warn\"\r\n}\r\n```\r\n\r\nExample error response:\r\n\r\n```json\r\n{ \r\n\t\"success\": false,\r\n\t\"minLogLevel\": null\r\n}\r\n```\r\n\r\n## Changelog\r\n\r\n### v2.0.0 [2019-12-20]\r\n\r\n**New features**\r\n- All logging output is now configured using the ```logSinks``` property. The library provides two commonly used loggers - ```consoleLogger``` and a ```remoteLogger``` that accepts the log url as a parameter. [Configuration](#configuration)\r\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\r\n- Log functions have a new optional ```containsPII``` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\r\n- A new helper function ```tagPII``` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\r\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\r\n\r\n**Breaking changes**\r\n- ```customLogSink``` configuration renamed to ```logSinks```\r\n- Removed ```disableRemoteLogging``` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\r\n- Removed ```logToConsole``` configuration. This is now configured as a log sink with a ```consoleLogger``` helper function.\r\n- Removed ```logUrl``` configuration. This is now configured as a log sink with a ```remoteLogger``` helper function.\r\n- Removed ```minLogLevel``` configuration. This is now configured per log sink.\r\n\r\n**Migration guide**\r\n``` javascript\r\nimport cleLogger from \"@prftesp/cle-logger\";\r\n\r\n// v1.0.0 and older config\r\ncleLogger.init({\r\n\tdisableRemoteLogSinks: true, // no direct replacement\r\n\tlogToConsole: true, // replaced by the \"console logger\" sink\r\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\r\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\r\n\tcustomLogSinks: [{\r\n\t\tname: \"custom sink\",\r\n\t\tlog: logs => {/* your custom logging */}\r\n\t}]\r\n});\r\n\r\n// v2.0.0 config\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"custom sink\",\r\n\t\tlog: logs => {/* your custom logging */},\r\n\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t// set to true to get feature parity with older version,\r\n\t\t// please consider your use case before setting\r\n\t\tpiiSafe: true\r\n\t},\r\n\t{\r\n\t\tname: \"console logger\",\r\n\t\tlog: consoleLogger,\r\n\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t// set to true to get feature parity with older version,\r\n\t\t// please consider your use case before setting\r\n\t\tpiiSafe: true\r\n\t},\r\n\t{\r\n\t\tname: \"remote logger\",\r\n\t\tlog: remoteLogger(\"http://log.output/api\"),\r\n\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t// set to true to get feature parity with older version,\r\n\t\t// please consider your use case before setting\r\n\t\tpiiSafe: true\t\t\r\n\t}]\r\n});\r\n```\r\n\r\n### v1.0.0 [2019-09-12]\r\n\r\n- Initial release\r\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.0.4-beta.1","_nodeVersion":"10.15.2","_npmVersion":"6.11.3","dist":{"integrity":"sha512-1hguemDJbXouuEpuHY1UhO0s3AB0TwyACX0hB+84AOqLXJomgjb7udWyAbONkho9GFMccfg6B3/WNuiaIbboHw==","shasum":"10b6bf72828ba3031790b7cd7ffc5cdc31c2f6da","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.0.4-beta.1.tgz","fileCount":17,"unpackedSize":67274,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeOJV3CRA9TVsSAnZWagAA+EUP/2g0fH8M/zJThSq6LWRL\nj7e0YP0N4CCmGVI3W8nh908H2xbG+i3K23kr1SGsPmTnOlc/ENVtbWaIbAyk\nHKJSRQWyniO8s7rpzq9IiVr2xnWtVnHkNrLK42Tm5yldMJ80bKjZyVRbnq6J\nihuS2t4ye7jhbHoi7IpDnS1j95IIc+Z0oa3kt72hP3B6akwz+pvDmWe9lYoK\ndBi0+nRn9vRYB2c/0U2f3/pcmkx5Io0g6Y1L7k37Yk+iYPLSSrGZPss91HG5\nO7xhnraTjFj9rbjVBh+2Bi61moE6c2t5LCcn2r2rD1WXKJ70kusPuFhe05NK\nY5YzdVXfNMO0nviWL3gZACLrPPTsK44HA6O5nJq5KHlzA0IzNFHRrC0ea7xs\nJKcUFCtwYf5xT6qwlOJ5zqrHb/cYoNRJSiw2MYtuO8f14znLWxKb4qsDdvJI\nn5ghaShug/ciplzIhWhMJ1p1ooAwEsGbuB7cb31cRVoV5c8QD7R83glJz9Bf\n/gTtjkfTs+pDQbj+aHQrM9SBbOxMJm1DT6mmoHCcYJQC+q6KqUIzl3QaMdaQ\n/XHLSozacOWgekTAsEyLwAF7jD9RvbHr/hOFo4RTcwu8tKPmhb/+ktcf8vlk\ndlVm9QmWmGxCANQIuk91kWFPMBfJeF1uvisJmNXSVyOz5kidrFEi0FFAuPVb\n34Sp\r\n=USj7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFeSP2U100B681p6lALi+Qpn2uV+FALpZaqYjMUn7B3/AiEAr/YfjhIaRNVoKdfZsv8NOlgYkZdTRTQ7B0tWIKi2zeo="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.0.4-beta.1_1580766583085_0.19519134657917658"},"_hasShrinkwrap":false},"2.0.4-beta.2":{"name":"@prftesp/cle-logger","version":"2.0.4-beta.2","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿CLE Logger\r\n===\r\n\r\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\r\n\r\n## Documentation\r\n\r\n- [Target Environment](#target-environment)\r\n- [Simple Example](#simple-example)\r\n- [Installation](#installation)\r\n- [Features](#features)\r\n- [Configuration](#configuration)\r\n- [API](#api)\r\n\t- [Log sinks](#log-sinks)\r\n\t- [Logging levels](#logging-levels)\r\n\t- [Logging parameters](#logging-parameters)\r\n\t- [Available component names](#available-component-names)\r\n\t- [Available custom property keys](#available-custom-property-keys)\r\n\t- [Log codes](#log-codes)\r\n\t- [Registering Twilio Actions](#registering-twilio-actions)\r\n\t- [Twilio Event Hooks](#twilio-event-hooks)\r\n\t- [Version](#version)\r\n- [Log Object](#log-object)\r\n- [Handling PII Data](#handling-pii-data)\r\n- [Advanced Configuration](#advanced-configuration)\r\n\t- [Remote logging](#remote-logging)\r\n- [Changelog](#changelog)\r\n\r\n## Target Environment\r\n\r\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\r\n\r\n## Simple Example\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"console sink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\n// will log message to console\r\ncleLogger.info(\"log message\");\r\n```\r\n\r\n## Installation\r\n\r\n```bash\r\n$ npm install @prftesp/cle-logger\r\n```\r\n\r\n## Features\r\n\r\n- Runs in Node.js and the browser\r\n- Remote logging\r\n- Multiple log sinks\r\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\r\n- Remote minimum log level overrides\r\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\r\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\r\n- Support for tagging logs with custom component names or correlation tokens\r\n- Send Twilio event hook data to a custom backend\r\n\r\n## Configuration\r\n\r\nTo configure the logger call the ```init``` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\r\n\r\n**Available options**\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// Twilio account sid, required for use with CLE\r\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\r\n\r\n\t// Will be associated with all logs,\r\n\t// if not provided, a unique uuid will be generated\r\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\r\n\t// An array of log sinks that will receive an array of log objects,\r\n\t// and can optionally return a promise\r\n\tlogSinks: [{\r\n\t\t// name of the log sink\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\t// log handler function\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\t// should this log sink accept PII data, defaults to false\r\n\t\tpiiSafe: true,\r\n\t\t// log level for the sink, defaults to INFO\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n\r\n\t// A way to suppress error messages if set to false, defaults to true\r\n\tlogErrorsToConsole: true,\r\n\r\n\t// If logging Twilio Flex Actions, provide a list of actions \r\n\t// to be excluded from logs\r\n\texcludedFlexActionNames: [\"SendTyping\"],\r\n\r\n\t// A custom endpoint to fetch log level overrides\r\n\t// NOTE: the provided url should always end with 'accountSid=',\r\n\t// the library will append the accountSid\r\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\r\n\r\n\t// An instance of ('@twilio/flex-ui').Manager\r\n\ttwilioFlexManager: manager,\r\n\r\n\t// To send data from Twilio event hooks, to a custom backend,\r\n\t// configure one or more endpoints for each event type\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\r\n\t}\r\n});\r\n```\r\n\r\n## API\r\n\r\nThe logger can be imported as an object in CommonJs or ES6 syntax:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n// or\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n```\r\n\r\nAlternatively, all top level members of the ```cleLogger``` are available as named exports and can be imported separately:\r\n\r\n```js\r\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tlogSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\r\n\r\n// vs\r\n\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tcleLogger.logSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\r\n```\r\n\r\n### Log sinks\r\n\r\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the ```logSinks``` config. It's an array of objects containing a ```name```, ```piiSafe``` flag to indicate if the sink can handle PII data without concern for privacy violations, ```logLevel```, and a ```log``` method. The ```log``` method will receive an array of log objects for you to handle. All provided log sinks will be executed in parallel. Log sinks should be async operations, and must return a Promise object. Built-in logger already do this, if you have a custom logger, make sure to return a promise.\r\n\r\nTwo built-in loggers are available out of the box. ```consoleLogger``` for logging to standard console, and ```remoteLogger``` for sending logs over http(s).\r\n\r\nLog sink object:\r\n\r\n``` js\r\n{\r\n\t// name of the log sink\r\n\tname: \"exampleConsoleSink\",\r\n\t// log handler function\r\n\tlog: logs => { \r\n\t\tconsole.log(logs);\r\n\t\treturn Promise.resolve();\r\n\t},\r\n\t// should this log sink accept PII data, defaults to false\r\n\tpiiSafe: true,\r\n\t// log level for the sink, defaults to INFO\r\n\tlogLevel: cleLogger.logLevels.VERBOSE\r\n}\r\n```\r\n\r\nExample log sinks:\r\n\r\n``` js\r\nconst fs = require(\"fs\");\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"fileSink\",\r\n\t\tlog: logs => {\r\n\t\t\tfs.appendFile(\r\n\t\t\t\t\"./log.txt\",\r\n\t\t\t\t`${JSON.stringify(logs)}`,\r\n\t\t\t\tconsole.log\r\n\t\t\t);\r\n\t\t\treturn Promise.resolve();\r\n\t\t}\r\n\t},\r\n\t{\r\n\t\tname: \"remoteSink\",\r\n\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\r\n\t\tlogLevel: cleLogger.logLevels.WARN,\r\n\t\tpiiSafe: true\r\n\t},\r\n\t{\r\n\t\tname: \"consoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}]\r\n});\r\n```\r\n\r\n### Logging levels\r\n\r\nLogging level constants can be found in ```cleLogger.logLevels```.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.logLevels.VERBOSE; // verbose\r\ncleLogger.logLevels.INFO; // info\r\ncleLogger.logLevels.WARN; // warn\r\ncleLogger.logLevels.ERROR; // error\r\ncleLogger.logLevels.CRITICAL; // critical\r\n```\r\n\r\nThere are functions available for each supported logging level:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(...);\r\ncleLogger.info(...);\r\ncleLogger.warn(...);\r\ncleLogger.error(...);\r\ncleLogger.critical(...);\r\n```\r\n\r\nThe functions are all async, return ```Promise<void>```, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\r\n\r\n### Logging parameters\r\n\r\nAll logging functions take the same parameters:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(\r\n\t// The only required property, no logs will be generated without it\r\n\t\"Log message\",\r\n\r\n\t// Any additional log data you would like to include with the log,\r\n\t// can be any data type\r\n\t{ data: \"test\" },\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\t// CLE uses it to index these properties in Application Insights\r\n\t// For CLE, try to use keys found in 'cleLogger.customPropertyKeys'\r\n\t{ userId: 123 },\r\n\r\n\t// Another useful way to 'tag' your logs for easier searching\r\n\t// Can be any string, but for CLE, try to use available options in 'cleLogger.componentNames'\r\n\t// If you would like more component names added, please let us know \r\n\t\"ProfileComponent\",\r\n\r\n\t// Log code, can be any number used to categorize logs,\r\n\t// there will be a separate document listing CLE codes\r\n\t70123,\r\n\r\n\t// Correlation token to be associated with this log only,\r\n\t// if one is not provided, the token supplied/generated during 'init' will be used\r\n\t\"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\t\r\n\t// Does the log contain PII data, defaults to false. If set to true, the log will be \r\n\t// passed in only to those log sinks that have 'piiSafe' set to true.\r\n\ttrue,\r\n);\r\n```\r\n\r\n### Available component names\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.componentNames.SESSION;\r\ncleLogger.componentNames.DIALER;\r\ncleLogger.componentNames.DASHBOARD;\r\ncleLogger.componentNames.AFTERCALL;\r\ncleLogger.componentNames.SUPERVISOR;\r\n```\r\n\r\n### Available custom property keys\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.customPropertyKeys.CHANNEL_SID;\r\ncleLogger.customPropertyKeys.TASK_SID;\r\ncleLogger.customPropertyKeys.CALL_SID;\r\ncleLogger.customPropertyKeys.WORKFLOW_SID;\r\ncleLogger.customPropertyKeys.WORKER_SID;\r\n```\r\n\r\n### Log codes\r\n\r\nTo categorize a log in one of the following categories, provide a log code in the appropriate range:\r\n\r\n- 100 000 - 109 999 => CALL_STATUS\r\n- 110 000 - 119 999 => IVR\r\n- 120 000 - 129 999 => ROUTING\r\n- 130 000 - 139 999 => AGENT_ACTION\r\n- 140 000 - 149 999 => TRANSFER\r\n- 150 000 - 159 999 => RM_INTEGRATION\r\n- 160 000 - 169 999 => WFM_INTEGRATION\r\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\r\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\r\n\r\n### Registering Twilio Actions\r\n\r\nExample in a Twilio Flex UI plugin:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\n/**\r\n * @param flex { typeof import('@twilio/flex-ui') }\r\n * @param manager { import('@twilio/flex-ui').Manager }\r\n */\r\ninit(flex, manager) {\r\n\tcleLogger.init({\r\n\t\taccountSid: \"your-account-sid\"\r\n\t});\r\n\r\n\tcleLogger.registerTwilioActions(flex);\r\n}\r\n```\r\n\r\n**Note**: A Twilio account sid must be provided during ```init``` with the ```accountSid``` parameter, otherwise the actions won't be logged.\r\n\r\nThe logger will loop through all actions found under ```flex.Actions.actions```, and attach ```after``` event handlers to log the action name and payload. Any private properties found in the payload (starting with '_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Any actions names configured in ```excludedFlexActionNames``` will not be included in the logs.\r\n\r\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\r\n\r\n**Twilio Actions log levels**\r\n\r\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of ```INFO```.\r\n\r\n``` js\r\nAcceptTask: INFO\r\nCancelTransfer: INFO\r\nCompleteTask: INFO\r\nHangupCall: INFO\r\nHideDirectory: VERBOSE\r\nHistoryGo: VERBOSE\r\nHistoryGoBack: VERBOSE\r\nHistoryGoForward: VERBOSE\r\nHistoryPush: VERBOSE\r\nHistoryReplace: VERBOSE\r\nHoldCall: INFO\r\nHoldParticipant: INFO\r\nKickParticipant: INFO\r\nLogout: INFO\r\nMonitorCall: INFO\r\nNavigateToView: INFO\r\nRejectTask: INFO\r\nSelectTask: INFO\r\nSelectTaskInSupervisor: INFO\r\nSelectWorkerInSupervisor: INFO\r\nSendMessage: INFO\r\nSendTyping: VERBOSE\r\nSetActivity: INFO\r\nSetInputText: VERBOSE\r\nShowDirectory: VERBOSE\r\nStopMonitoringCall: INFO\r\nToggleMute: INFO\r\nToggleSidebar: VERBOSE\r\nTransferTask: INFO\r\nUnholdCall: INFO\r\nUnholdParticipant: INFO\r\nWrapupTask: INFO\r\n```\r\n\r\n### Twilio Event Hooks\r\n\r\nCurrently, Twilio only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with ```twilioEventHooks``` option, and use the following functions to send event data to a custom endpoint.\r\n\r\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is ```application/x-www-form-urlencoded``` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// unrelated properties omitted\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\r\n\t}\r\n});\r\n\r\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\r\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\r\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\r\n```\r\n\r\nExample Azure Function hook for debugger events:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\nmodule.exports = async function (context, req) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\t}\r\n\t});\r\n\r\n\t// log to console\r\n\tcleLogger.info(\"Received Twilio debugger event\");\r\n\t\r\n\t// send event data to a configured url\r\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\r\n};\r\n```\r\n\r\n**NOTE:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to ```x-www-form-urlencoded``` format.\r\n\r\nExample Twilio function task router relay using the ```form-urlencoded``` npm package to convert the data to ```x-www-form-urlencoded``` format:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\nconst formurlencoded = require('form-urlencoded').default;\r\n\r\nexports.handler = async function(context, event, callback) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\t}\r\n\t});\r\n\r\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\r\n\r\n\tcallback();\r\n};\r\n```\r\n\r\n### Version\r\n\r\nVersion will be automatically included in all logs.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.version; // 1.0.0\r\n```\r\n\r\n## Log Object\r\n\r\n```js\r\n{\r\n\t// Account sid if provided during init\r\n\taccountSid: string;\r\n\r\n\t// Any additional log data to be included with the log\r\n\tadditionalData: unknown;\r\n\t\r\n\t// Component name, if provided\r\n\tcomponentName: string;\r\n\r\n\t// Generated or configured token\r\n\tcorrelationToken: string;\r\n\r\n\t// Current UTC datetime in ISO 8601 format\r\n\teventDate: string;\r\n\r\n\t// Log level\r\n\tlevel: string;\r\n\r\n\t// Log code, if provided\r\n\tlogCode: number;\r\n\r\n\t// Log message\r\n\tmessage: string;\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\tproperties: object;\r\n\r\n\t// BrowserFunction if the environment is the browser\r\n\t// CloudFunction if the environment is Node.js\r\n\tsource: string;\r\n\r\n\t// Logging library version \r\n\tloggerVersion: string;\r\n}\r\n```\r\n\r\n## Handling PII Data\r\n\r\nThe logger provides support for marking logs as containing PII (personally identifiable data), and configuring each log sink to indicate if it can handle PII data. By default, log sinks are not marked as PII safe, meaning they will not receive logs marked as containing PII. Marking a log sink as PII safe, means that the logger should be able to handle PII data without privacy violations (for example, a remote log sink that sends data to an API that masks PII data would be considered safe).\r\n\r\nAdditionally, the logger exports a function ```tagPII``` which can be used to tag parts of a string message as PII for easier masking. Tags can be used by backend systems to mask the data with a simple regex search and replace.\r\n\r\nExample tagged output:\r\n\r\n```js\r\nimport { tagPII } from \"@prftesp/cle-logger\";\r\n\r\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\r\n```\r\n\r\n## Advanced Configuration\r\n\r\n### Remote logging\r\n\r\nThe library provides a way to provide you own remote log url, or to remotely override the min log level. This can be useful for temporarily setting the log level to verbose in a production environment, for quick troubleshooting.\r\n\r\n**Remote log endpoint**\r\n\r\nTo implement your own remote logging endpoint, all you have to do is accept ```POST``` requests, with a JSON payload of an array of [log objects](#log-object), and return either ```{ success: true }``` or ```{ success: false }```. The endpoint should always return a ```200 OK``` status code. A good use-case would be leveraging a serverless api platform to output logs to a queue.\r\n\r\nExample Azure Function:\r\n\r\n```js\r\nmodule.exports = async function (context, req) {\r\n    context.log(req.body); // body will have the log array\r\n\r\n    context.res = {\r\n        status: 200,\r\n        body: { success: true }\r\n    };\r\n};\r\n```\r\n\r\n**Remote log level override**\r\n\r\nThe ```minLogLevelUrl``` configuration property is used to provide an endpoint that accepts ```GET``` requests and a ```accountSid``` query string parameter. ```accountSid``` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the ```minLogLevel``` configuration property. If null is returned, the ```minLogLevel``` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a ```200 OK``` status code and use the ```success``` property to indicate failure.\r\n\r\nExample success response:\r\n\r\n```json\r\n{ \r\n\t\"success\": true,\r\n\t\"minLogLevel\": \"warn\"\r\n}\r\n```\r\n\r\nExample error response:\r\n\r\n```json\r\n{ \r\n\t\"success\": false,\r\n\t\"minLogLevel\": null\r\n}\r\n```\r\n\r\n## Changelog\r\n\r\n### v2.0.0 [2019-12-20]\r\n\r\n**New features**\r\n- All logging output is now configured using the ```logSinks``` property. The library provides two commonly used loggers - ```consoleLogger``` and a ```remoteLogger``` that accepts the log url as a parameter. [Configuration](#configuration)\r\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\r\n- Log functions have a new optional ```containsPII``` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\r\n- A new helper function ```tagPII``` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\r\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\r\n\r\n**Breaking changes**\r\n- ```customLogSink``` configuration renamed to ```logSinks```\r\n- Removed ```disableRemoteLogging``` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\r\n- Removed ```logToConsole``` configuration. This is now configured as a log sink with a ```consoleLogger``` helper function.\r\n- Removed ```logUrl``` configuration. This is now configured as a log sink with a ```remoteLogger``` helper function.\r\n- Removed ```minLogLevel``` configuration. This is now configured per log sink.\r\n\r\n**Migration guide**\r\n``` javascript\r\nimport cleLogger from \"@prftesp/cle-logger\";\r\n\r\n// v1.0.0 and older config\r\ncleLogger.init({\r\n\tdisableRemoteLogSinks: true, // no direct replacement\r\n\tlogToConsole: true, // replaced by the \"console logger\" sink\r\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\r\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\r\n\tcustomLogSinks: [{\r\n\t\tname: \"custom sink\",\r\n\t\tlog: logs => {/* your custom logging */}\r\n\t}]\r\n});\r\n\r\n// v2.0.0 config\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"custom sink\",\r\n\t\tlog: logs => {/* your custom logging */},\r\n\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t// set to true to get feature parity with older version,\r\n\t\t// please consider your use case before setting\r\n\t\tpiiSafe: true\r\n\t},\r\n\t{\r\n\t\tname: \"console logger\",\r\n\t\tlog: consoleLogger,\r\n\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t// set to true to get feature parity with older version,\r\n\t\t// please consider your use case before setting\r\n\t\tpiiSafe: true\r\n\t},\r\n\t{\r\n\t\tname: \"remote logger\",\r\n\t\tlog: remoteLogger(\"http://log.output/api\"),\r\n\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t// set to true to get feature parity with older version,\r\n\t\t// please consider your use case before setting\r\n\t\tpiiSafe: true\t\t\r\n\t}]\r\n});\r\n```\r\n\r\n### v1.0.0 [2019-09-12]\r\n\r\n- Initial release\r\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.0.4-beta.2","_nodeVersion":"10.15.2","_npmVersion":"6.11.3","dist":{"integrity":"sha512-LZ+28gynvDBnRJ8pTB9SKnP3/pNNRvYfgDJXi7vOjSvzr6s39YwE0JkvVz0BB7wlgEQr1tq1vJxJq8AZp0WUwg==","shasum":"5c3e9b84cfa0e0435269604eb07f0c127cf8216a","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.0.4-beta.2.tgz","fileCount":17,"unpackedSize":67238,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeOJXkCRA9TVsSAnZWagAAj2oQAJ+S4pvDXukoAFf0ls/8\npvvoow7/LFKnXoAc5QF8O4X4P6bcUKIuOzVTdEPZsikuJ7t2TC1ONODYWoKY\n6w0stI/Z1pguH5+cINwE0v5pVd04h0qZXNT69EyE82mcin4qslkSOCxLUSHk\nSUGQW8HCyIc6/I1GPSVO4V6XYhC3T2rW7ALEB5UzQrxZV+zO2OnHieZxuMRD\nnMz1r15WyAhLSDNiX0trqfc210mxp8s9uBcOLITsOL1bI3os+mZB7weqvEDs\nCakBaSnaFXNL6869bvmxxaUfxwLUq/rtgHvpn9W2gR2d7BB/RXDFFcP/cTve\nGlUqEWgnKPZ5FKouNBiDv9scbU7Znc6SkEOWPCrHY8HgV5hb8e1lFb6DKJsh\nsz4v010dvL7ZREaAmC9pd60TyhU2WY9BPuCjQdLaTeN9FI+hZ0ZAs09/DjrE\nDbP7Wn2cdaBNFJr5VJYwZIk3o3Lc4/jDwmH71PZ1Gb8EFS83dLDrupAHsEWh\nu7A49NkEqw5nORr4uEFUwGV/DmP7FJQMKir4hF9YY7XVbEnCApgJJEQB7rEH\nbF9B/CfYaTqa78Lf4DPGDJtJ/RVpOqWTd1cB3NPkpRIjL8idfNTxaQEFNGem\ni+Drvzc95ju+EcHMh4WVFBdo3cR3Cn0R+7U1YH1efGYPOKBZvTCSXv94KvmY\nQ922\r\n=GkJb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDiv9R5o3HO6ie+fCaaIj2qqnzCzUER0MTPdpnjhWRatQIhAK0qEllde3i8yvPuuQDvuhPDJ69NixwL7l6f175MDJkd"}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.0.4-beta.2_1580766691563_0.1547578482205687"},"_hasShrinkwrap":false},"2.0.4-beta.3":{"name":"@prftesp/cle-logger","version":"2.0.4-beta.3","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿CLE Logger\r\n===\r\n\r\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\r\n\r\n## Documentation\r\n\r\n- [Target Environment](#target-environment)\r\n- [Simple Example](#simple-example)\r\n- [Installation](#installation)\r\n- [Features](#features)\r\n- [Configuration](#configuration)\r\n- [API](#api)\r\n\t- [Log sinks](#log-sinks)\r\n\t- [Logging levels](#logging-levels)\r\n\t- [Logging parameters](#logging-parameters)\r\n\t- [Available component names](#available-component-names)\r\n\t- [Available custom property keys](#available-custom-property-keys)\r\n\t- [Log codes](#log-codes)\r\n\t- [Registering Twilio Actions](#registering-twilio-actions)\r\n\t- [Twilio Event Hooks](#twilio-event-hooks)\r\n\t- [Version](#version)\r\n- [Log Object](#log-object)\r\n- [Handling PII Data](#handling-pii-data)\r\n- [Advanced Configuration](#advanced-configuration)\r\n\t- [Remote logging](#remote-logging)\r\n- [Changelog](#changelog)\r\n\r\n## Target Environment\r\n\r\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\r\n\r\n## Simple Example\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"console sink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\n// will log message to console\r\ncleLogger.info(\"log message\");\r\n```\r\n\r\n## Installation\r\n\r\n```bash\r\n$ npm install @prftesp/cle-logger\r\n```\r\n\r\n## Features\r\n\r\n- Runs in Node.js and the browser\r\n- Remote logging\r\n- Multiple log sinks\r\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\r\n- Remote minimum log level overrides\r\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\r\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\r\n- Support for tagging logs with custom component names or correlation tokens\r\n- Send Twilio event hook data to a custom backend\r\n\r\n## Configuration\r\n\r\nTo configure the logger call the ```init``` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\r\n\r\n**Available options**\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// Twilio account sid, required for use with CLE\r\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\r\n\r\n\t// Will be associated with all logs,\r\n\t// if not provided, a unique uuid will be generated\r\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\r\n\t// An array of log sinks that will receive an array of log objects,\r\n\t// and can optionally return a promise\r\n\tlogSinks: [{\r\n\t\t// name of the log sink\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\t// log handler function\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\t// should this log sink accept PII data, defaults to false\r\n\t\tpiiSafe: true,\r\n\t\t// log level for the sink, defaults to INFO\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n\r\n\t// A way to suppress error messages if set to false, defaults to true\r\n\tlogErrorsToConsole: true,\r\n\r\n\t// If logging Twilio Flex Actions, provide a list of actions \r\n\t// to be excluded from logs\r\n\texcludedFlexActionNames: [\"SendTyping\"],\r\n\r\n\t// A custom endpoint to fetch log level overrides\r\n\t// NOTE: the provided url should always end with 'accountSid=',\r\n\t// the library will append the accountSid\r\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\r\n\r\n\t// An instance of ('@twilio/flex-ui').Manager\r\n\ttwilioFlexManager: manager,\r\n\r\n\t// To send data from Twilio event hooks, to a custom backend,\r\n\t// configure one or more endpoints for each event type\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\r\n\t}\r\n});\r\n```\r\n\r\n## API\r\n\r\nThe logger can be imported as an object in CommonJs or ES6 syntax:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n// or\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n```\r\n\r\nAlternatively, all top level members of the ```cleLogger``` are available as named exports and can be imported separately:\r\n\r\n```js\r\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tlogSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\r\n\r\n// vs\r\n\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tcleLogger.logSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\r\n```\r\n\r\n### Log sinks\r\n\r\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the ```logSinks``` config. It's an array of objects containing a ```name```, ```piiSafe``` flag to indicate if the sink can handle PII data without concern for privacy violations, ```logLevel```, and a ```log``` method. The ```log``` method will receive an array of log objects for you to handle. All provided log sinks will be executed in parallel. Log sinks should be async operations, and must return a Promise object. Built-in logger already do this, if you have a custom logger, make sure to return a promise.\r\n\r\nTwo built-in loggers are available out of the box. ```consoleLogger``` for logging to standard console, and ```remoteLogger``` for sending logs over http(s).\r\n\r\nLog sink object:\r\n\r\n``` js\r\n{\r\n\t// name of the log sink\r\n\tname: \"exampleConsoleSink\",\r\n\t// log handler function\r\n\tlog: logs => { \r\n\t\tconsole.log(logs);\r\n\t\treturn Promise.resolve();\r\n\t},\r\n\t// should this log sink accept PII data, defaults to false\r\n\tpiiSafe: true,\r\n\t// log level for the sink, defaults to INFO\r\n\tlogLevel: cleLogger.logLevels.VERBOSE\r\n}\r\n```\r\n\r\nExample log sinks:\r\n\r\n``` js\r\nconst fs = require(\"fs\");\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"fileSink\",\r\n\t\tlog: logs => {\r\n\t\t\tfs.appendFile(\r\n\t\t\t\t\"./log.txt\",\r\n\t\t\t\t`${JSON.stringify(logs)}`,\r\n\t\t\t\tconsole.log\r\n\t\t\t);\r\n\t\t\treturn Promise.resolve();\r\n\t\t}\r\n\t},\r\n\t{\r\n\t\tname: \"remoteSink\",\r\n\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\r\n\t\tlogLevel: cleLogger.logLevels.WARN,\r\n\t\tpiiSafe: true\r\n\t},\r\n\t{\r\n\t\tname: \"consoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}]\r\n});\r\n```\r\n\r\n### Logging levels\r\n\r\nLogging level constants can be found in ```cleLogger.logLevels```.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.logLevels.VERBOSE; // verbose\r\ncleLogger.logLevels.INFO; // info\r\ncleLogger.logLevels.WARN; // warn\r\ncleLogger.logLevels.ERROR; // error\r\ncleLogger.logLevels.CRITICAL; // critical\r\n```\r\n\r\nThere are functions available for each supported logging level:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(...);\r\ncleLogger.info(...);\r\ncleLogger.warn(...);\r\ncleLogger.error(...);\r\ncleLogger.critical(...);\r\n```\r\n\r\nThe functions are all async, return ```Promise<void>```, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\r\n\r\n### Logging parameters\r\n\r\nAll logging functions take the same parameters:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(\r\n\t// The only required property, no logs will be generated without it\r\n\t\"Log message\",\r\n\r\n\t// Any additional log data you would like to include with the log,\r\n\t// can be any data type\r\n\t{ data: \"test\" },\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\t// CLE uses it to index these properties in Application Insights\r\n\t// For CLE, try to use keys found in 'cleLogger.customPropertyKeys'\r\n\t{ userId: 123 },\r\n\r\n\t// Another useful way to 'tag' your logs for easier searching\r\n\t// Can be any string, but for CLE, try to use available options in 'cleLogger.componentNames'\r\n\t// If you would like more component names added, please let us know \r\n\t\"ProfileComponent\",\r\n\r\n\t// Log code, can be any number used to categorize logs,\r\n\t// there will be a separate document listing CLE codes\r\n\t70123,\r\n\r\n\t// Correlation token to be associated with this log only,\r\n\t// if one is not provided, the token supplied/generated during 'init' will be used\r\n\t\"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\t\r\n\t// Does the log contain PII data, defaults to false. If set to true, the log will be \r\n\t// passed in only to those log sinks that have 'piiSafe' set to true.\r\n\ttrue,\r\n);\r\n```\r\n\r\n### Available component names\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.componentNames.SESSION;\r\ncleLogger.componentNames.DIALER;\r\ncleLogger.componentNames.DASHBOARD;\r\ncleLogger.componentNames.AFTERCALL;\r\ncleLogger.componentNames.SUPERVISOR;\r\n```\r\n\r\n### Available custom property keys\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.customPropertyKeys.CHANNEL_SID;\r\ncleLogger.customPropertyKeys.TASK_SID;\r\ncleLogger.customPropertyKeys.CALL_SID;\r\ncleLogger.customPropertyKeys.WORKFLOW_SID;\r\ncleLogger.customPropertyKeys.WORKER_SID;\r\n```\r\n\r\n### Log codes\r\n\r\nTo categorize a log in one of the following categories, provide a log code in the appropriate range:\r\n\r\n- 100 000 - 109 999 => CALL_STATUS\r\n- 110 000 - 119 999 => IVR\r\n- 120 000 - 129 999 => ROUTING\r\n- 130 000 - 139 999 => AGENT_ACTION\r\n- 140 000 - 149 999 => TRANSFER\r\n- 150 000 - 159 999 => RM_INTEGRATION\r\n- 160 000 - 169 999 => WFM_INTEGRATION\r\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\r\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\r\n\r\n### Registering Twilio Actions\r\n\r\nExample in a Twilio Flex UI plugin:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\n/**\r\n * @param flex { typeof import('@twilio/flex-ui') }\r\n * @param manager { import('@twilio/flex-ui').Manager }\r\n */\r\ninit(flex, manager) {\r\n\tcleLogger.init({\r\n\t\taccountSid: \"your-account-sid\"\r\n\t});\r\n\r\n\tcleLogger.registerTwilioActions(flex);\r\n}\r\n```\r\n\r\n**Note**: A Twilio account sid must be provided during ```init``` with the ```accountSid``` parameter, otherwise the actions won't be logged.\r\n\r\nThe logger will loop through all actions found under ```flex.Actions.actions```, and attach ```after``` event handlers to log the action name and payload. Any private properties found in the payload (starting with '_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Any actions names configured in ```excludedFlexActionNames``` will not be included in the logs.\r\n\r\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\r\n\r\n**Twilio Actions log levels**\r\n\r\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of ```INFO```.\r\n\r\n``` js\r\nAcceptTask: INFO\r\nCancelTransfer: INFO\r\nCompleteTask: INFO\r\nHangupCall: INFO\r\nHideDirectory: VERBOSE\r\nHistoryGo: VERBOSE\r\nHistoryGoBack: VERBOSE\r\nHistoryGoForward: VERBOSE\r\nHistoryPush: VERBOSE\r\nHistoryReplace: VERBOSE\r\nHoldCall: INFO\r\nHoldParticipant: INFO\r\nKickParticipant: INFO\r\nLogout: INFO\r\nMonitorCall: INFO\r\nNavigateToView: INFO\r\nRejectTask: INFO\r\nSelectTask: INFO\r\nSelectTaskInSupervisor: INFO\r\nSelectWorkerInSupervisor: INFO\r\nSendMessage: INFO\r\nSendTyping: VERBOSE\r\nSetActivity: INFO\r\nSetInputText: VERBOSE\r\nShowDirectory: VERBOSE\r\nStopMonitoringCall: INFO\r\nToggleMute: INFO\r\nToggleSidebar: VERBOSE\r\nTransferTask: INFO\r\nUnholdCall: INFO\r\nUnholdParticipant: INFO\r\nWrapupTask: INFO\r\n```\r\n\r\n### Twilio Event Hooks\r\n\r\nCurrently, Twilio only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with ```twilioEventHooks``` option, and use the following functions to send event data to a custom endpoint.\r\n\r\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is ```application/x-www-form-urlencoded``` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// unrelated properties omitted\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\r\n\t}\r\n});\r\n\r\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\r\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\r\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\r\n```\r\n\r\nExample Azure Function hook for debugger events:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\nmodule.exports = async function (context, req) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\t}\r\n\t});\r\n\r\n\t// log to console\r\n\tcleLogger.info(\"Received Twilio debugger event\");\r\n\t\r\n\t// send event data to a configured url\r\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\r\n};\r\n```\r\n\r\n**NOTE:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to ```x-www-form-urlencoded``` format.\r\n\r\nExample Twilio function task router relay using the ```form-urlencoded``` npm package to convert the data to ```x-www-form-urlencoded``` format:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\nconst formurlencoded = require('form-urlencoded').default;\r\n\r\nexports.handler = async function(context, event, callback) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\t}\r\n\t});\r\n\r\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\r\n\r\n\tcallback();\r\n};\r\n```\r\n\r\n### Version\r\n\r\nVersion will be automatically included in all logs.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.version; // 1.0.0\r\n```\r\n\r\n## Log Object\r\n\r\n```js\r\n{\r\n\t// Account sid if provided during init\r\n\taccountSid: string;\r\n\r\n\t// Any additional log data to be included with the log\r\n\tadditionalData: unknown;\r\n\t\r\n\t// Component name, if provided\r\n\tcomponentName: string;\r\n\r\n\t// Generated or configured token\r\n\tcorrelationToken: string;\r\n\r\n\t// Current UTC datetime in ISO 8601 format\r\n\teventDate: string;\r\n\r\n\t// Log level\r\n\tlevel: string;\r\n\r\n\t// Log code, if provided\r\n\tlogCode: number;\r\n\r\n\t// Log message\r\n\tmessage: string;\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\tproperties: object;\r\n\r\n\t// BrowserFunction if the environment is the browser\r\n\t// CloudFunction if the environment is Node.js\r\n\tsource: string;\r\n\r\n\t// Logging library version \r\n\tloggerVersion: string;\r\n}\r\n```\r\n\r\n## Handling PII Data\r\n\r\nThe logger provides support for marking logs as containing PII (personally identifiable data), and configuring each log sink to indicate if it can handle PII data. By default, log sinks are not marked as PII safe, meaning they will not receive logs marked as containing PII. Marking a log sink as PII safe, means that the logger should be able to handle PII data without privacy violations (for example, a remote log sink that sends data to an API that masks PII data would be considered safe).\r\n\r\nAdditionally, the logger exports a function ```tagPII``` which can be used to tag parts of a string message as PII for easier masking. Tags can be used by backend systems to mask the data with a simple regex search and replace.\r\n\r\nExample tagged output:\r\n\r\n```js\r\nimport { tagPII } from \"@prftesp/cle-logger\";\r\n\r\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\r\n```\r\n\r\n## Advanced Configuration\r\n\r\n### Remote logging\r\n\r\nThe library provides a way to provide you own remote log url, or to remotely override the min log level. This can be useful for temporarily setting the log level to verbose in a production environment, for quick troubleshooting.\r\n\r\n**Remote log endpoint**\r\n\r\nTo implement your own remote logging endpoint, all you have to do is accept ```POST``` requests, with a JSON payload of an array of [log objects](#log-object), and return either ```{ success: true }``` or ```{ success: false }```. The endpoint should always return a ```200 OK``` status code. A good use-case would be leveraging a serverless api platform to output logs to a queue.\r\n\r\nExample Azure Function:\r\n\r\n```js\r\nmodule.exports = async function (context, req) {\r\n    context.log(req.body); // body will have the log array\r\n\r\n    context.res = {\r\n        status: 200,\r\n        body: { success: true }\r\n    };\r\n};\r\n```\r\n\r\n**Remote log level override**\r\n\r\nThe ```minLogLevelUrl``` configuration property is used to provide an endpoint that accepts ```GET``` requests and a ```accountSid``` query string parameter. ```accountSid``` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the ```minLogLevel``` configuration property. If null is returned, the ```minLogLevel``` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a ```200 OK``` status code and use the ```success``` property to indicate failure.\r\n\r\nExample success response:\r\n\r\n```json\r\n{ \r\n\t\"success\": true,\r\n\t\"minLogLevel\": \"warn\"\r\n}\r\n```\r\n\r\nExample error response:\r\n\r\n```json\r\n{ \r\n\t\"success\": false,\r\n\t\"minLogLevel\": null\r\n}\r\n```\r\n\r\n## Changelog\r\n\r\n### v2.0.0 [2019-12-20]\r\n\r\n**New features**\r\n- All logging output is now configured using the ```logSinks``` property. The library provides two commonly used loggers - ```consoleLogger``` and a ```remoteLogger``` that accepts the log url as a parameter. [Configuration](#configuration)\r\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\r\n- Log functions have a new optional ```containsPII``` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\r\n- A new helper function ```tagPII``` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\r\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\r\n\r\n**Breaking changes**\r\n- ```customLogSink``` configuration renamed to ```logSinks```\r\n- Removed ```disableRemoteLogging``` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\r\n- Removed ```logToConsole``` configuration. This is now configured as a log sink with a ```consoleLogger``` helper function.\r\n- Removed ```logUrl``` configuration. This is now configured as a log sink with a ```remoteLogger``` helper function.\r\n- Removed ```minLogLevel``` configuration. This is now configured per log sink.\r\n\r\n**Migration guide**\r\n``` javascript\r\nimport cleLogger from \"@prftesp/cle-logger\";\r\n\r\n// v1.0.0 and older config\r\ncleLogger.init({\r\n\tdisableRemoteLogSinks: true, // no direct replacement\r\n\tlogToConsole: true, // replaced by the \"console logger\" sink\r\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\r\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\r\n\tcustomLogSinks: [{\r\n\t\tname: \"custom sink\",\r\n\t\tlog: logs => {/* your custom logging */}\r\n\t}]\r\n});\r\n\r\n// v2.0.0 config\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"custom sink\",\r\n\t\tlog: logs => {/* your custom logging */},\r\n\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t// set to true to get feature parity with older version,\r\n\t\t// please consider your use case before setting\r\n\t\tpiiSafe: true\r\n\t},\r\n\t{\r\n\t\tname: \"console logger\",\r\n\t\tlog: consoleLogger,\r\n\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t// set to true to get feature parity with older version,\r\n\t\t// please consider your use case before setting\r\n\t\tpiiSafe: true\r\n\t},\r\n\t{\r\n\t\tname: \"remote logger\",\r\n\t\tlog: remoteLogger(\"http://log.output/api\"),\r\n\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t// set to true to get feature parity with older version,\r\n\t\t// please consider your use case before setting\r\n\t\tpiiSafe: true\t\t\r\n\t}]\r\n});\r\n```\r\n\r\n### v1.0.0 [2019-09-12]\r\n\r\n- Initial release\r\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.0.4-beta.3","_nodeVersion":"10.15.2","_npmVersion":"6.11.3","dist":{"integrity":"sha512-4N6KTQkj8xgBbo2EJFAQ/Z9mBaWljm3ihrynSaNARtC21w+8EEwH5GIXchjyV4p+u1bDMsj4L45TRxRkMceGjg==","shasum":"3b6d22966daff4e1c5711c3df967e12fd5d92260","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.0.4-beta.3.tgz","fileCount":17,"unpackedSize":67264,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeOJnLCRA9TVsSAnZWagAAq1oP/2EeoeyBt+U8+5RgI1Qz\nX8uQsezTc1sQAuhwyYCzM+y7RfOI7Xtm1lFAVebvLLvQZBjPVSgvao47U7zc\nsK0kXGeZ9boTGSsnldr4iLQSEnSbVVIsAckzFJdIJPdorUga0nVz5s7Pcc/U\nFmIgVk+Qy86MB6ccEm3PXvp0BL2ZBzuPRFAHbuIDTrt7QX1Rj2F0Mjp0ZeJS\nKbjlgf+xByrBf9TSToy5b5ySTG5c8CthBCfE3AQOo+QNcYDGBoTH/jwWQsQF\nNL3C0YPnIuDI450i2F4K94ofToBxOuLO8aD4cqaaKUARKrRVkMf0NQqbstGB\nfL8R3+9aLVQ58L8nl6IbLCFawRSjelw2b7nvcvDB6y1M1uM/2EF2sY5jbYCy\n7R59g3TJAtYgx1H9gDPBiLstfzy2GrBlfd79PZskCkI181Oa44FLKlcQiRUa\n2zq2cDfuCvdU7Zk/+YrwoAAfV60GlkqV0/4CZYSZhNF2NqhaluUNZNQPNaIk\nw1j/rhnhbXIJVsR9gKK0MejckqeExxjHbHAPqn+YoTe6vAjoC+wrLhZ/lkCg\nbP3Tb7C1GR5c02YJTTIUIfcsbY85YXaqN8561OKEzKdcftGeVuNlabPrR35M\ncOLK9/3sv82uoSxCo/d9XgA34vvuWtR/QnIebDcMmqLQz7G5jfz1salNoEWe\nlmry\r\n=KzqF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIANPEA6Y3wwIWFf/CS1r234ChSEb7m6s9UDko/ubbphFAiEAnI2FLEMM6CfHX6MgcuniDwn0OoYi1YyDNIQJTfGOW1I="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.0.4-beta.3_1580767691330_0.5208750171825476"},"_hasShrinkwrap":false},"2.0.4-beta.4":{"name":"@prftesp/cle-logger","version":"2.0.4-beta.4","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿CLE Logger\r\n===\r\n\r\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\r\n\r\n## Documentation\r\n\r\n- [Target Environment](#target-environment)\r\n- [Simple Example](#simple-example)\r\n- [Installation](#installation)\r\n- [Features](#features)\r\n- [Configuration](#configuration)\r\n- [API](#api)\r\n\t- [Log sinks](#log-sinks)\r\n\t- [Logging levels](#logging-levels)\r\n\t- [Logging parameters](#logging-parameters)\r\n\t- [Available component names](#available-component-names)\r\n\t- [Available custom property keys](#available-custom-property-keys)\r\n\t- [Log codes](#log-codes)\r\n\t- [Registering Twilio Actions](#registering-twilio-actions)\r\n\t- [Twilio Event Hooks](#twilio-event-hooks)\r\n\t- [Version](#version)\r\n- [Log Object](#log-object)\r\n- [Handling PII Data](#handling-pii-data)\r\n- [Advanced Configuration](#advanced-configuration)\r\n\t- [Remote logging](#remote-logging)\r\n- [Changelog](#changelog)\r\n\r\n## Target Environment\r\n\r\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\r\n\r\n## Simple Example\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"console sink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\n// will log message to console\r\ncleLogger.info(\"log message\");\r\n```\r\n\r\n## Installation\r\n\r\n```bash\r\n$ npm install @prftesp/cle-logger\r\n```\r\n\r\n## Features\r\n\r\n- Runs in Node.js and the browser\r\n- Remote logging\r\n- Multiple log sinks\r\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\r\n- Remote minimum log level overrides\r\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\r\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\r\n- Support for tagging logs with custom component names or correlation tokens\r\n- Send Twilio event hook data to a custom backend\r\n\r\n## Configuration\r\n\r\nTo configure the logger call the ```init``` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\r\n\r\n**Available options**\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// Twilio account sid, required for use with CLE\r\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\r\n\r\n\t// Will be associated with all logs,\r\n\t// if not provided, a unique uuid will be generated\r\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\r\n\t// An array of log sinks that will receive an array of log objects,\r\n\t// and can optionally return a promise\r\n\tlogSinks: [{\r\n\t\t// name of the log sink\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\t// log handler function\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\t// should this log sink accept PII data, defaults to false\r\n\t\tpiiSafe: true,\r\n\t\t// log level for the sink, defaults to INFO\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n\r\n\t// A way to suppress error messages if set to false, defaults to true\r\n\tlogErrorsToConsole: true,\r\n\r\n\t// If logging Twilio Flex Actions, provide a list of actions \r\n\t// to be excluded from logs\r\n\texcludedFlexActionNames: [\"SendTyping\"],\r\n\r\n\t// A custom endpoint to fetch log level overrides\r\n\t// NOTE: the provided url should always end with 'accountSid=',\r\n\t// the library will append the accountSid\r\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\r\n\r\n\t// An instance of ('@twilio/flex-ui').Manager\r\n\ttwilioFlexManager: manager,\r\n\r\n\t// To send data from Twilio event hooks, to a custom backend,\r\n\t// configure one or more endpoints for each event type\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\r\n\t}\r\n});\r\n```\r\n\r\n## API\r\n\r\nThe logger can be imported as an object in CommonJs or ES6 syntax:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n// or\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n```\r\n\r\nAlternatively, all top level members of the ```cleLogger``` are available as named exports and can be imported separately:\r\n\r\n```js\r\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tlogSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\r\n\r\n// vs\r\n\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tcleLogger.logSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\r\n```\r\n\r\n### Log sinks\r\n\r\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the ```logSinks``` config. It's an array of objects containing a ```name```, ```piiSafe``` flag to indicate if the sink can handle PII data without concern for privacy violations, ```logLevel```, and a ```log``` method. The ```log``` method will receive an array of log objects for you to handle. All provided log sinks will be executed in parallel. Log sinks should be async operations, and must return a Promise object. Built-in logger already do this, if you have a custom logger, make sure to return a promise.\r\n\r\nTwo built-in loggers are available out of the box. ```consoleLogger``` for logging to standard console, and ```remoteLogger``` for sending logs over http(s).\r\n\r\nLog sink object:\r\n\r\n``` js\r\n{\r\n\t// name of the log sink\r\n\tname: \"exampleConsoleSink\",\r\n\t// log handler function\r\n\tlog: logs => { \r\n\t\tconsole.log(logs);\r\n\t\treturn Promise.resolve();\r\n\t},\r\n\t// should this log sink accept PII data, defaults to false\r\n\tpiiSafe: true,\r\n\t// log level for the sink, defaults to INFO\r\n\tlogLevel: cleLogger.logLevels.VERBOSE\r\n}\r\n```\r\n\r\nExample log sinks:\r\n\r\n``` js\r\nconst fs = require(\"fs\");\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"fileSink\",\r\n\t\tlog: logs => {\r\n\t\t\tfs.appendFile(\r\n\t\t\t\t\"./log.txt\",\r\n\t\t\t\t`${JSON.stringify(logs)}`,\r\n\t\t\t\tconsole.log\r\n\t\t\t);\r\n\t\t\treturn Promise.resolve();\r\n\t\t}\r\n\t},\r\n\t{\r\n\t\tname: \"remoteSink\",\r\n\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\r\n\t\tlogLevel: cleLogger.logLevels.WARN,\r\n\t\tpiiSafe: true\r\n\t},\r\n\t{\r\n\t\tname: \"consoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}]\r\n});\r\n```\r\n\r\n### Logging levels\r\n\r\nLogging level constants can be found in ```cleLogger.logLevels```.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.logLevels.VERBOSE; // verbose\r\ncleLogger.logLevels.INFO; // info\r\ncleLogger.logLevels.WARN; // warn\r\ncleLogger.logLevels.ERROR; // error\r\ncleLogger.logLevels.CRITICAL; // critical\r\n```\r\n\r\nThere are functions available for each supported logging level:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(...);\r\ncleLogger.info(...);\r\ncleLogger.warn(...);\r\ncleLogger.error(...);\r\ncleLogger.critical(...);\r\n```\r\n\r\nThe functions are all async, return ```Promise<void>```, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\r\n\r\n### Logging parameters\r\n\r\nAll logging functions take the same parameters:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(\r\n\t// The only required property, no logs will be generated without it\r\n\t\"Log message\",\r\n\r\n\t// Any additional log data you would like to include with the log,\r\n\t// can be any data type\r\n\t{ data: \"test\" },\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\t// CLE uses it to index these properties in Application Insights\r\n\t// For CLE, try to use keys found in 'cleLogger.customPropertyKeys'\r\n\t{ userId: 123 },\r\n\r\n\t// Another useful way to 'tag' your logs for easier searching\r\n\t// Can be any string, but for CLE, try to use available options in 'cleLogger.componentNames'\r\n\t// If you would like more component names added, please let us know \r\n\t\"ProfileComponent\",\r\n\r\n\t// Log code, can be any number used to categorize logs,\r\n\t// there will be a separate document listing CLE codes\r\n\t70123,\r\n\r\n\t// Correlation token to be associated with this log only,\r\n\t// if one is not provided, the token supplied/generated during 'init' will be used\r\n\t\"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\t\r\n\t// Does the log contain PII data, defaults to false. If set to true, the log will be \r\n\t// passed in only to those log sinks that have 'piiSafe' set to true.\r\n\ttrue,\r\n);\r\n```\r\n\r\n### Available component names\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.componentNames.SESSION;\r\ncleLogger.componentNames.DIALER;\r\ncleLogger.componentNames.DASHBOARD;\r\ncleLogger.componentNames.AFTERCALL;\r\ncleLogger.componentNames.SUPERVISOR;\r\n```\r\n\r\n### Available custom property keys\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.customPropertyKeys.CHANNEL_SID;\r\ncleLogger.customPropertyKeys.TASK_SID;\r\ncleLogger.customPropertyKeys.CALL_SID;\r\ncleLogger.customPropertyKeys.WORKFLOW_SID;\r\ncleLogger.customPropertyKeys.WORKER_SID;\r\n```\r\n\r\n### Log codes\r\n\r\nTo categorize a log in one of the following categories, provide a log code in the appropriate range:\r\n\r\n- 100 000 - 109 999 => CALL_STATUS\r\n- 110 000 - 119 999 => IVR\r\n- 120 000 - 129 999 => ROUTING\r\n- 130 000 - 139 999 => AGENT_ACTION\r\n- 140 000 - 149 999 => TRANSFER\r\n- 150 000 - 159 999 => RM_INTEGRATION\r\n- 160 000 - 169 999 => WFM_INTEGRATION\r\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\r\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\r\n\r\n### Registering Twilio Actions\r\n\r\nExample in a Twilio Flex UI plugin:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\n/**\r\n * @param flex { typeof import('@twilio/flex-ui') }\r\n * @param manager { import('@twilio/flex-ui').Manager }\r\n */\r\ninit(flex, manager) {\r\n\tcleLogger.init({\r\n\t\taccountSid: \"your-account-sid\"\r\n\t});\r\n\r\n\tcleLogger.registerTwilioActions(flex);\r\n}\r\n```\r\n\r\n**Note**: A Twilio account sid must be provided during ```init``` with the ```accountSid``` parameter, otherwise the actions won't be logged.\r\n\r\nThe logger will loop through all actions found under ```flex.Actions.actions```, and attach ```after``` event handlers to log the action name and payload. Any private properties found in the payload (starting with '_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Any actions names configured in ```excludedFlexActionNames``` will not be included in the logs.\r\n\r\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\r\n\r\n**Twilio Actions log levels**\r\n\r\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of ```INFO```.\r\n\r\n``` js\r\nAcceptTask: INFO\r\nCancelTransfer: INFO\r\nCompleteTask: INFO\r\nHangupCall: INFO\r\nHideDirectory: VERBOSE\r\nHistoryGo: VERBOSE\r\nHistoryGoBack: VERBOSE\r\nHistoryGoForward: VERBOSE\r\nHistoryPush: VERBOSE\r\nHistoryReplace: VERBOSE\r\nHoldCall: INFO\r\nHoldParticipant: INFO\r\nKickParticipant: INFO\r\nLogout: INFO\r\nMonitorCall: INFO\r\nNavigateToView: INFO\r\nRejectTask: INFO\r\nSelectTask: INFO\r\nSelectTaskInSupervisor: INFO\r\nSelectWorkerInSupervisor: INFO\r\nSendMessage: INFO\r\nSendTyping: VERBOSE\r\nSetActivity: INFO\r\nSetInputText: VERBOSE\r\nShowDirectory: VERBOSE\r\nStopMonitoringCall: INFO\r\nToggleMute: INFO\r\nToggleSidebar: VERBOSE\r\nTransferTask: INFO\r\nUnholdCall: INFO\r\nUnholdParticipant: INFO\r\nWrapupTask: INFO\r\n```\r\n\r\n### Twilio Event Hooks\r\n\r\nCurrently, Twilio only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with ```twilioEventHooks``` option, and use the following functions to send event data to a custom endpoint.\r\n\r\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is ```application/x-www-form-urlencoded``` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// unrelated properties omitted\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\r\n\t}\r\n});\r\n\r\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\r\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\r\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\r\n```\r\n\r\nExample Azure Function hook for debugger events:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\nmodule.exports = async function (context, req) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\t}\r\n\t});\r\n\r\n\t// log to console\r\n\tcleLogger.info(\"Received Twilio debugger event\");\r\n\t\r\n\t// send event data to a configured url\r\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\r\n};\r\n```\r\n\r\n**NOTE:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to ```x-www-form-urlencoded``` format.\r\n\r\nExample Twilio function task router relay using the ```form-urlencoded``` npm package to convert the data to ```x-www-form-urlencoded``` format:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\nconst formurlencoded = require('form-urlencoded').default;\r\n\r\nexports.handler = async function(context, event, callback) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\t}\r\n\t});\r\n\r\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\r\n\r\n\tcallback();\r\n};\r\n```\r\n\r\n### Version\r\n\r\nVersion will be automatically included in all logs.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.version; // 1.0.0\r\n```\r\n\r\n## Log Object\r\n\r\n```js\r\n{\r\n\t// Account sid if provided during init\r\n\taccountSid: string;\r\n\r\n\t// Any additional log data to be included with the log\r\n\tadditionalData: unknown;\r\n\t\r\n\t// Component name, if provided\r\n\tcomponentName: string;\r\n\r\n\t// Generated or configured token\r\n\tcorrelationToken: string;\r\n\r\n\t// Current UTC datetime in ISO 8601 format\r\n\teventDate: string;\r\n\r\n\t// Log level\r\n\tlevel: string;\r\n\r\n\t// Log code, if provided\r\n\tlogCode: number;\r\n\r\n\t// Log message\r\n\tmessage: string;\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\tproperties: object;\r\n\r\n\t// BrowserFunction if the environment is the browser\r\n\t// CloudFunction if the environment is Node.js\r\n\tsource: string;\r\n\r\n\t// Logging library version \r\n\tloggerVersion: string;\r\n}\r\n```\r\n\r\n## Handling PII Data\r\n\r\nThe logger provides support for marking logs as containing PII (personally identifiable data), and configuring each log sink to indicate if it can handle PII data. By default, log sinks are not marked as PII safe, meaning they will not receive logs marked as containing PII. Marking a log sink as PII safe, means that the logger should be able to handle PII data without privacy violations (for example, a remote log sink that sends data to an API that masks PII data would be considered safe).\r\n\r\nAdditionally, the logger exports a function ```tagPII``` which can be used to tag parts of a string message as PII for easier masking. Tags can be used by backend systems to mask the data with a simple regex search and replace.\r\n\r\nExample tagged output:\r\n\r\n```js\r\nimport { tagPII } from \"@prftesp/cle-logger\";\r\n\r\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\r\n```\r\n\r\n## Advanced Configuration\r\n\r\n### Remote logging\r\n\r\nThe library provides a way to provide you own remote log url, or to remotely override the min log level. This can be useful for temporarily setting the log level to verbose in a production environment, for quick troubleshooting.\r\n\r\n**Remote log endpoint**\r\n\r\nTo implement your own remote logging endpoint, all you have to do is accept ```POST``` requests, with a JSON payload of an array of [log objects](#log-object), and return either ```{ success: true }``` or ```{ success: false }```. The endpoint should always return a ```200 OK``` status code. A good use-case would be leveraging a serverless api platform to output logs to a queue.\r\n\r\nExample Azure Function:\r\n\r\n```js\r\nmodule.exports = async function (context, req) {\r\n    context.log(req.body); // body will have the log array\r\n\r\n    context.res = {\r\n        status: 200,\r\n        body: { success: true }\r\n    };\r\n};\r\n```\r\n\r\n**Remote log level override**\r\n\r\nThe ```minLogLevelUrl``` configuration property is used to provide an endpoint that accepts ```GET``` requests and a ```accountSid``` query string parameter. ```accountSid``` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the ```minLogLevel``` configuration property. If null is returned, the ```minLogLevel``` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a ```200 OK``` status code and use the ```success``` property to indicate failure.\r\n\r\nExample success response:\r\n\r\n```json\r\n{ \r\n\t\"success\": true,\r\n\t\"minLogLevel\": \"warn\"\r\n}\r\n```\r\n\r\nExample error response:\r\n\r\n```json\r\n{ \r\n\t\"success\": false,\r\n\t\"minLogLevel\": null\r\n}\r\n```\r\n\r\n## Changelog\r\n\r\n### v2.0.0 [2019-12-20]\r\n\r\n**New features**\r\n- All logging output is now configured using the ```logSinks``` property. The library provides two commonly used loggers - ```consoleLogger``` and a ```remoteLogger``` that accepts the log url as a parameter. [Configuration](#configuration)\r\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\r\n- Log functions have a new optional ```containsPII``` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\r\n- A new helper function ```tagPII``` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\r\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\r\n\r\n**Breaking changes**\r\n- ```customLogSink``` configuration renamed to ```logSinks```\r\n- Removed ```disableRemoteLogging``` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\r\n- Removed ```logToConsole``` configuration. This is now configured as a log sink with a ```consoleLogger``` helper function.\r\n- Removed ```logUrl``` configuration. This is now configured as a log sink with a ```remoteLogger``` helper function.\r\n- Removed ```minLogLevel``` configuration. This is now configured per log sink.\r\n\r\n**Migration guide**\r\n``` javascript\r\nimport cleLogger from \"@prftesp/cle-logger\";\r\n\r\n// v1.0.0 and older config\r\ncleLogger.init({\r\n\tdisableRemoteLogSinks: true, // no direct replacement\r\n\tlogToConsole: true, // replaced by the \"console logger\" sink\r\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\r\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\r\n\tcustomLogSinks: [{\r\n\t\tname: \"custom sink\",\r\n\t\tlog: logs => {/* your custom logging */}\r\n\t}]\r\n});\r\n\r\n// v2.0.0 config\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"custom sink\",\r\n\t\tlog: logs => {/* your custom logging */},\r\n\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t// set to true to get feature parity with older version,\r\n\t\t// please consider your use case before setting\r\n\t\tpiiSafe: true\r\n\t},\r\n\t{\r\n\t\tname: \"console logger\",\r\n\t\tlog: consoleLogger,\r\n\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t// set to true to get feature parity with older version,\r\n\t\t// please consider your use case before setting\r\n\t\tpiiSafe: true\r\n\t},\r\n\t{\r\n\t\tname: \"remote logger\",\r\n\t\tlog: remoteLogger(\"http://log.output/api\"),\r\n\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t// set to true to get feature parity with older version,\r\n\t\t// please consider your use case before setting\r\n\t\tpiiSafe: true\t\t\r\n\t}]\r\n});\r\n```\r\n\r\n### v1.0.0 [2019-09-12]\r\n\r\n- Initial release\r\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.0.4-beta.4","_nodeVersion":"10.15.2","_npmVersion":"6.11.3","dist":{"integrity":"sha512-h1FNXV9JPaTl4/8BEE2P80X86+jzN34aPHeT5WpHt92NWiEy/VLp5osLZo6meJRuoJdot6VZE3dMI54L+8N6hQ==","shasum":"a5542cd0f030798bec01075a89cf7aee9382252e","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.0.4-beta.4.tgz","fileCount":17,"unpackedSize":67305,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeOJqJCRA9TVsSAnZWagAApLsQAIaJzmF+5wbbUjrJzWhI\noIT64c8edFMZmhxO4TXAsfD27Zxr0WCIPp5Mp7dyLZMNNk8bjxfAYhdFH3kv\nBVqzjilX24hwhhxNYVsIZ6zbTQxm/trY+FUrl0oj0EZgwL9bekkW6sSZtR+k\n/7WZwEqtCUx/KjmRIUuZDPX6y16DOXjekqSSnHLXVOjO35oiaRLNQQwpPI3X\nhFTgGJbqO9b/s/EFDIn2Ql2eet5pIpvamqby5hnykqcxFqxf9alIb5kxoy8i\nl3awYvpw8gNZkCJ89Vkd1YOJ7B7lh1g5Bve+bysebfah05hwT9MSt7shS6jo\nqnviRSxAi4o+ZxvIuyN/hec70X2M+1TOeorV2hMxBBLPiTOPy0F/I2Yc55+9\nudwecznaWPjFxb0KIuOsh6gGBIvobfHzfzLU5WYUFYJhGdNwmeLqdzJP3iF6\ny0+1EZKQNnuFXaC6mg90eyrd5f8x3xHtmwlAos49gf4QW7ZXQ+uM7nYNn5In\ngJiMzrpGpQV3Pt+34RVqAl+94kAQHqrUbkslr4GZVpWbtB+cJt6oCqPT4KAY\nAM8vmPDT2yHTrgqsI8spZsTsXdZfkt4i7XOM4FUjpm5ZL0jTynM8ZNqTcHuD\nJ8vhguVADKS+5Bu2Q8bpYWjaYfPzvn6Ux5zlVisDQngaiajbILC/RO70JdWL\n5qGc\r\n=3+ud\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDL5kmufqM3eYG/E8Afut7Mhr9IbOmYsLvfMpTz7rfFtQIhANKw8NrQJHbk1jC4PZQxqMoxJmGdSVg5UylXSS03tsWY"}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.0.4-beta.4_1580767880580_0.5876341936831888"},"_hasShrinkwrap":false},"2.0.4-beta.5":{"name":"@prftesp/cle-logger","version":"2.0.4-beta.5","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿CLE Logger\r\n===\r\n\r\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\r\n\r\n## Documentation\r\n\r\n- [Target Environment](#target-environment)\r\n- [Simple Example](#simple-example)\r\n- [Installation](#installation)\r\n- [Features](#features)\r\n- [Configuration](#configuration)\r\n- [API](#api)\r\n\t- [Log sinks](#log-sinks)\r\n\t- [Logging levels](#logging-levels)\r\n\t- [Logging parameters](#logging-parameters)\r\n\t- [Available component names](#available-component-names)\r\n\t- [Available custom property keys](#available-custom-property-keys)\r\n\t- [Log codes](#log-codes)\r\n\t- [Registering Twilio Actions](#registering-twilio-actions)\r\n\t- [Twilio Event Hooks](#twilio-event-hooks)\r\n\t- [Version](#version)\r\n- [Log Object](#log-object)\r\n- [Handling PII Data](#handling-pii-data)\r\n- [Advanced Configuration](#advanced-configuration)\r\n\t- [Remote logging](#remote-logging)\r\n- [Changelog](#changelog)\r\n\r\n## Target Environment\r\n\r\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\r\n\r\n## Simple Example\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"console sink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\n// will log message to console\r\ncleLogger.info(\"log message\");\r\n```\r\n\r\n## Installation\r\n\r\n```bash\r\n$ npm install @prftesp/cle-logger\r\n```\r\n\r\n## Features\r\n\r\n- Runs in Node.js and the browser\r\n- Remote logging\r\n- Multiple log sinks\r\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\r\n- Remote minimum log level overrides\r\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\r\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\r\n- Support for tagging logs with custom component names or correlation tokens\r\n- Send Twilio event hook data to a custom backend\r\n\r\n## Configuration\r\n\r\nTo configure the logger call the ```init``` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\r\n\r\n**Available options**\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// Twilio account sid, required for use with CLE\r\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\r\n\r\n\t// Will be associated with all logs,\r\n\t// if not provided, a unique uuid will be generated\r\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\r\n\t// An array of log sinks that will receive an array of log objects,\r\n\t// and can optionally return a promise\r\n\tlogSinks: [{\r\n\t\t// name of the log sink\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\t// log handler function\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\t// should this log sink accept PII data, defaults to false\r\n\t\tpiiSafe: true,\r\n\t\t// log level for the sink, defaults to INFO\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n\r\n\t// A way to suppress error messages if set to false, defaults to true\r\n\tlogErrorsToConsole: true,\r\n\r\n\t// If logging Twilio Flex Actions, provide a list of actions \r\n\t// to be excluded from logs\r\n\texcludedFlexActionNames: [\"SendTyping\"],\r\n\r\n\t// A custom endpoint to fetch log level overrides\r\n\t// NOTE: the provided url should always end with 'accountSid=',\r\n\t// the library will append the accountSid\r\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\r\n\r\n\t// An instance of ('@twilio/flex-ui').Manager\r\n\ttwilioFlexManager: manager,\r\n\r\n\t// To send data from Twilio event hooks, to a custom backend,\r\n\t// configure one or more endpoints for each event type\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\r\n\t}\r\n});\r\n```\r\n\r\n## API\r\n\r\nThe logger can be imported as an object in CommonJs or ES6 syntax:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n// or\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n```\r\n\r\nAlternatively, all top level members of the ```cleLogger``` are available as named exports and can be imported separately:\r\n\r\n```js\r\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tlogSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\r\n\r\n// vs\r\n\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tcleLogger.logSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\r\n```\r\n\r\n### Log sinks\r\n\r\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the ```logSinks``` config. It's an array of objects containing a ```name```, ```piiSafe``` flag to indicate if the sink can handle PII data without concern for privacy violations, ```logLevel```, and a ```log``` method. The ```log``` method will receive an array of log objects for you to handle. All provided log sinks will be executed in parallel. Log sinks should be async operations, and must return a Promise object. Built-in logger already do this, if you have a custom logger, make sure to return a promise.\r\n\r\nTwo built-in loggers are available out of the box. ```consoleLogger``` for logging to standard console, and ```remoteLogger``` for sending logs over http(s).\r\n\r\nLog sink object:\r\n\r\n``` js\r\n{\r\n\t// name of the log sink\r\n\tname: \"exampleConsoleSink\",\r\n\t// log handler function\r\n\tlog: logs => { \r\n\t\tconsole.log(logs);\r\n\t\treturn Promise.resolve();\r\n\t},\r\n\t// should this log sink accept PII data, defaults to false\r\n\tpiiSafe: true,\r\n\t// log level for the sink, defaults to INFO\r\n\tlogLevel: cleLogger.logLevels.VERBOSE\r\n}\r\n```\r\n\r\nExample log sinks:\r\n\r\n``` js\r\nconst fs = require(\"fs\");\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"fileSink\",\r\n\t\tlog: logs => {\r\n\t\t\tfs.appendFile(\r\n\t\t\t\t\"./log.txt\",\r\n\t\t\t\t`${JSON.stringify(logs)}`,\r\n\t\t\t\tconsole.log\r\n\t\t\t);\r\n\t\t\treturn Promise.resolve();\r\n\t\t}\r\n\t},\r\n\t{\r\n\t\tname: \"remoteSink\",\r\n\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\r\n\t\tlogLevel: cleLogger.logLevels.WARN,\r\n\t\tpiiSafe: true\r\n\t},\r\n\t{\r\n\t\tname: \"consoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}]\r\n});\r\n```\r\n\r\n### Logging levels\r\n\r\nLogging level constants can be found in ```cleLogger.logLevels```.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.logLevels.VERBOSE; // verbose\r\ncleLogger.logLevels.INFO; // info\r\ncleLogger.logLevels.WARN; // warn\r\ncleLogger.logLevels.ERROR; // error\r\ncleLogger.logLevels.CRITICAL; // critical\r\n```\r\n\r\nThere are functions available for each supported logging level:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(...);\r\ncleLogger.info(...);\r\ncleLogger.warn(...);\r\ncleLogger.error(...);\r\ncleLogger.critical(...);\r\n```\r\n\r\nThe functions are all async, return ```Promise<void>```, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\r\n\r\n### Logging parameters\r\n\r\nAll logging functions take the same parameters:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(\r\n\t// The only required property, no logs will be generated without it\r\n\t\"Log message\",\r\n\r\n\t// Any additional log data you would like to include with the log,\r\n\t// can be any data type\r\n\t{ data: \"test\" },\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\t// CLE uses it to index these properties in Application Insights\r\n\t// For CLE, try to use keys found in 'cleLogger.customPropertyKeys'\r\n\t{ userId: 123 },\r\n\r\n\t// Another useful way to 'tag' your logs for easier searching\r\n\t// Can be any string, but for CLE, try to use available options in 'cleLogger.componentNames'\r\n\t// If you would like more component names added, please let us know \r\n\t\"ProfileComponent\",\r\n\r\n\t// Log code, can be any number used to categorize logs,\r\n\t// there will be a separate document listing CLE codes\r\n\t70123,\r\n\r\n\t// Correlation token to be associated with this log only,\r\n\t// if one is not provided, the token supplied/generated during 'init' will be used\r\n\t\"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\t\r\n\t// Does the log contain PII data, defaults to false. If set to true, the log will be \r\n\t// passed in only to those log sinks that have 'piiSafe' set to true.\r\n\ttrue,\r\n);\r\n```\r\n\r\n### Available component names\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.componentNames.SESSION;\r\ncleLogger.componentNames.DIALER;\r\ncleLogger.componentNames.DASHBOARD;\r\ncleLogger.componentNames.AFTERCALL;\r\ncleLogger.componentNames.SUPERVISOR;\r\n```\r\n\r\n### Available custom property keys\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.customPropertyKeys.CHANNEL_SID;\r\ncleLogger.customPropertyKeys.TASK_SID;\r\ncleLogger.customPropertyKeys.CALL_SID;\r\ncleLogger.customPropertyKeys.WORKFLOW_SID;\r\ncleLogger.customPropertyKeys.WORKER_SID;\r\n```\r\n\r\n### Log codes\r\n\r\nTo categorize a log in one of the following categories, provide a log code in the appropriate range:\r\n\r\n- 100 000 - 109 999 => CALL_STATUS\r\n- 110 000 - 119 999 => IVR\r\n- 120 000 - 129 999 => ROUTING\r\n- 130 000 - 139 999 => AGENT_ACTION\r\n- 140 000 - 149 999 => TRANSFER\r\n- 150 000 - 159 999 => RM_INTEGRATION\r\n- 160 000 - 169 999 => WFM_INTEGRATION\r\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\r\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\r\n\r\n### Registering Twilio Actions\r\n\r\nExample in a Twilio Flex UI plugin:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\n/**\r\n * @param flex { typeof import('@twilio/flex-ui') }\r\n * @param manager { import('@twilio/flex-ui').Manager }\r\n */\r\ninit(flex, manager) {\r\n\tcleLogger.init({\r\n\t\taccountSid: \"your-account-sid\"\r\n\t});\r\n\r\n\tcleLogger.registerTwilioActions(flex);\r\n}\r\n```\r\n\r\n**Note**: A Twilio account sid must be provided during ```init``` with the ```accountSid``` parameter, otherwise the actions won't be logged.\r\n\r\nThe logger will loop through all actions found under ```flex.Actions.actions```, and attach ```after``` event handlers to log the action name and payload. Any private properties found in the payload (starting with '_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Any actions names configured in ```excludedFlexActionNames``` will not be included in the logs.\r\n\r\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\r\n\r\n**Twilio Actions log levels**\r\n\r\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of ```INFO```.\r\n\r\n``` js\r\nAcceptTask: INFO\r\nCancelTransfer: INFO\r\nCompleteTask: INFO\r\nHangupCall: INFO\r\nHideDirectory: VERBOSE\r\nHistoryGo: VERBOSE\r\nHistoryGoBack: VERBOSE\r\nHistoryGoForward: VERBOSE\r\nHistoryPush: VERBOSE\r\nHistoryReplace: VERBOSE\r\nHoldCall: INFO\r\nHoldParticipant: INFO\r\nKickParticipant: INFO\r\nLogout: INFO\r\nMonitorCall: INFO\r\nNavigateToView: INFO\r\nRejectTask: INFO\r\nSelectTask: INFO\r\nSelectTaskInSupervisor: INFO\r\nSelectWorkerInSupervisor: INFO\r\nSendMessage: INFO\r\nSendTyping: VERBOSE\r\nSetActivity: INFO\r\nSetInputText: VERBOSE\r\nShowDirectory: VERBOSE\r\nStopMonitoringCall: INFO\r\nToggleMute: INFO\r\nToggleSidebar: VERBOSE\r\nTransferTask: INFO\r\nUnholdCall: INFO\r\nUnholdParticipant: INFO\r\nWrapupTask: INFO\r\n```\r\n\r\n### Twilio Event Hooks\r\n\r\nCurrently, Twilio only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with ```twilioEventHooks``` option, and use the following functions to send event data to a custom endpoint.\r\n\r\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is ```application/x-www-form-urlencoded``` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// unrelated properties omitted\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\r\n\t}\r\n});\r\n\r\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\r\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\r\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\r\n```\r\n\r\nExample Azure Function hook for debugger events:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\nmodule.exports = async function (context, req) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\t}\r\n\t});\r\n\r\n\t// log to console\r\n\tcleLogger.info(\"Received Twilio debugger event\");\r\n\t\r\n\t// send event data to a configured url\r\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\r\n};\r\n```\r\n\r\n**NOTE:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to ```x-www-form-urlencoded``` format.\r\n\r\nExample Twilio function task router relay using the ```form-urlencoded``` npm package to convert the data to ```x-www-form-urlencoded``` format:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\nconst formurlencoded = require('form-urlencoded').default;\r\n\r\nexports.handler = async function(context, event, callback) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\t}\r\n\t});\r\n\r\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\r\n\r\n\tcallback();\r\n};\r\n```\r\n\r\n### Version\r\n\r\nVersion will be automatically included in all logs.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.version; // 1.0.0\r\n```\r\n\r\n## Log Object\r\n\r\n```js\r\n{\r\n\t// Account sid if provided during init\r\n\taccountSid: string;\r\n\r\n\t// Any additional log data to be included with the log\r\n\tadditionalData: unknown;\r\n\t\r\n\t// Component name, if provided\r\n\tcomponentName: string;\r\n\r\n\t// Generated or configured token\r\n\tcorrelationToken: string;\r\n\r\n\t// Current UTC datetime in ISO 8601 format\r\n\teventDate: string;\r\n\r\n\t// Log level\r\n\tlevel: string;\r\n\r\n\t// Log code, if provided\r\n\tlogCode: number;\r\n\r\n\t// Log message\r\n\tmessage: string;\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\tproperties: object;\r\n\r\n\t// BrowserFunction if the environment is the browser\r\n\t// CloudFunction if the environment is Node.js\r\n\tsource: string;\r\n\r\n\t// Logging library version \r\n\tloggerVersion: string;\r\n}\r\n```\r\n\r\n## Handling PII Data\r\n\r\nThe logger provides support for marking logs as containing PII (personally identifiable data), and configuring each log sink to indicate if it can handle PII data. By default, log sinks are not marked as PII safe, meaning they will not receive logs marked as containing PII. Marking a log sink as PII safe, means that the logger should be able to handle PII data without privacy violations (for example, a remote log sink that sends data to an API that masks PII data would be considered safe).\r\n\r\nAdditionally, the logger exports a function ```tagPII``` which can be used to tag parts of a string message as PII for easier masking. Tags can be used by backend systems to mask the data with a simple regex search and replace.\r\n\r\nExample tagged output:\r\n\r\n```js\r\nimport { tagPII } from \"@prftesp/cle-logger\";\r\n\r\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\r\n```\r\n\r\n## Advanced Configuration\r\n\r\n### Remote logging\r\n\r\nThe library provides a way to provide you own remote log url, or to remotely override the min log level. This can be useful for temporarily setting the log level to verbose in a production environment, for quick troubleshooting.\r\n\r\n**Remote log endpoint**\r\n\r\nTo implement your own remote logging endpoint, all you have to do is accept ```POST``` requests, with a JSON payload of an array of [log objects](#log-object), and return either ```{ success: true }``` or ```{ success: false }```. The endpoint should always return a ```200 OK``` status code. A good use-case would be leveraging a serverless api platform to output logs to a queue.\r\n\r\nExample Azure Function:\r\n\r\n```js\r\nmodule.exports = async function (context, req) {\r\n    context.log(req.body); // body will have the log array\r\n\r\n    context.res = {\r\n        status: 200,\r\n        body: { success: true }\r\n    };\r\n};\r\n```\r\n\r\n**Remote log level override**\r\n\r\nThe ```minLogLevelUrl``` configuration property is used to provide an endpoint that accepts ```GET``` requests and a ```accountSid``` query string parameter. ```accountSid``` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the ```minLogLevel``` configuration property. If null is returned, the ```minLogLevel``` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a ```200 OK``` status code and use the ```success``` property to indicate failure.\r\n\r\nExample success response:\r\n\r\n```json\r\n{ \r\n\t\"success\": true,\r\n\t\"minLogLevel\": \"warn\"\r\n}\r\n```\r\n\r\nExample error response:\r\n\r\n```json\r\n{ \r\n\t\"success\": false,\r\n\t\"minLogLevel\": null\r\n}\r\n```\r\n\r\n## Changelog\r\n\r\n### v2.0.4\r\n\r\n**Bug fixes**\r\n- Fixed issue with logging to console in Twilio Functions\r\n\r\n### v2.0.0 [2019-12-20]\r\n\r\n**New features**\r\n- All logging output is now configured using the ```logSinks``` property. The library provides two commonly used loggers - ```consoleLogger``` and a ```remoteLogger``` that accepts the log url as a parameter. [Configuration](#configuration)\r\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\r\n- Log functions have a new optional ```containsPII``` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\r\n- A new helper function ```tagPII``` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\r\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\r\n\r\n**Breaking changes**\r\n- ```customLogSink``` configuration renamed to ```logSinks```\r\n- Removed ```disableRemoteLogging``` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\r\n- Removed ```logToConsole``` configuration. This is now configured as a log sink with a ```consoleLogger``` helper function.\r\n- Removed ```logUrl``` configuration. This is now configured as a log sink with a ```remoteLogger``` helper function.\r\n- Removed ```minLogLevel``` configuration. This is now configured per log sink.\r\n\r\n**Migration guide**\r\n``` javascript\r\nimport cleLogger from \"@prftesp/cle-logger\";\r\n\r\n// v1.0.0 and older config\r\ncleLogger.init({\r\n\tdisableRemoteLogSinks: true, // no direct replacement\r\n\tlogToConsole: true, // replaced by the \"console logger\" sink\r\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\r\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\r\n\tcustomLogSinks: [{\r\n\t\tname: \"custom sink\",\r\n\t\tlog: logs => {/* your custom logging */}\r\n\t}]\r\n});\r\n\r\n// v2.0.0 config\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"custom sink\",\r\n\t\tlog: logs => {/* your custom logging */},\r\n\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t// set to true to get feature parity with older version,\r\n\t\t// please consider your use case before setting\r\n\t\tpiiSafe: true\r\n\t},\r\n\t{\r\n\t\tname: \"console logger\",\r\n\t\tlog: consoleLogger,\r\n\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t// set to true to get feature parity with older version,\r\n\t\t// please consider your use case before setting\r\n\t\tpiiSafe: true\r\n\t},\r\n\t{\r\n\t\tname: \"remote logger\",\r\n\t\tlog: remoteLogger(\"http://log.output/api\"),\r\n\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t// set to true to get feature parity with older version,\r\n\t\t// please consider your use case before setting\r\n\t\tpiiSafe: true\t\t\r\n\t}]\r\n});\r\n```\r\n\r\n### v1.0.0 [2019-09-12]\r\n\r\n- Initial release\r\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.0.4-beta.5","_nodeVersion":"10.15.2","_npmVersion":"6.11.3","dist":{"integrity":"sha512-9PtAlVNa4sAaMYI2DevpC1Z/qgnu7Y9SrddHllDpolONHJkDOgLY1DTGVRNr4Wa5kd4WvDUE2Rsqah/ykomBGw==","shasum":"0979c92c0956fb4eb47fc98367bcf8c59fe18ba6","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.0.4-beta.5.tgz","fileCount":17,"unpackedSize":67635,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeOJ0UCRA9TVsSAnZWagAAygUP/iQ1PtpoaYsTPjtQ5g74\nQdcC7dcgsMoGKhiviX+SmjlL/7M/TRzMcYr6LkEbbEEc5bZQvKgHoC8+ABW2\nYpQSNvP/bT24E55rKmMnx9+AOGcfXubtnxOQN8Eq7iek51z5Vilz0Zx++QW4\nB8idGeMQrpjJw3ef99gMGVz3xneBJ5lVXLsPGey3KP3LusF2BXZcFPK+Gr9e\nN4kivx9F0kNqGbYXPdTeaafA9bQdSBpiUs7hh+aEB+R5kI8b4KYezDe+UYe3\n2xD7ySy893W7Z5/8Y3es2x1U7OGZvLeyxPJiiJU5BMdaPhbr0Gh+jLI5rw/S\nYN9v9IQ9hLrhTz7p/0tFYhbSYzrQKn1XnZKsFWzNwyIp4uo4hZiVMPIYUy/T\n7RvHNOgwpg+JF/Mgksy33ViopjAUCmN0PV26kHrT3qwaq9d2F7Yzje8B+QwT\nH1o8hiB56hxQwxR1iCIWQJVOu12BE90Vcqvf7X+3LUnaBmT6DTRCwp+KMeh6\nJzTipnD4/RhvnztvbE/5GTp06Gc3r2wL5XKG6+cIn10aW2VZRE40FMjAFacF\nv5A97pJewiAMrWHi7V+BRsHq6BmHgbcj1gRQRPuXZ4UFWWwfJvrvdv8YK9uU\nc4cmAQvUp3yhvutO79O8ASDODhLn2YVIWvIUrkiEyd5xJ2iqznAotdv67D36\nufg9\r\n=oW1x\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD4Mq6XprUq1wqRR/85Ktq7lzppTIZznzAS0LUeHzqJgAIhAJC6eTEsJKtsSA0IhbqyuQFnvIt8RDml7aVrMUVON84l"}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.0.4-beta.5_1580768527018_0.17191763715825648"},"_hasShrinkwrap":false},"2.0.4-beta.6":{"name":"@prftesp/cle-logger","version":"2.0.4-beta.6","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿CLE Logger\r\n===\r\n\r\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\r\n\r\n## Documentation\r\n\r\n- [Target Environment](#target-environment)\r\n- [Simple Example](#simple-example)\r\n- [Installation](#installation)\r\n- [Features](#features)\r\n- [Configuration](#configuration)\r\n- [API](#api)\r\n\t- [Log sinks](#log-sinks)\r\n\t- [Logging levels](#logging-levels)\r\n\t- [Logging parameters](#logging-parameters)\r\n\t- [Available component names](#available-component-names)\r\n\t- [Available custom property keys](#available-custom-property-keys)\r\n\t- [Log codes](#log-codes)\r\n\t- [Registering Twilio Actions](#registering-twilio-actions)\r\n\t- [Twilio Event Hooks](#twilio-event-hooks)\r\n\t- [Version](#version)\r\n- [Log Object](#log-object)\r\n- [Handling PII Data](#handling-pii-data)\r\n- [Advanced Configuration](#advanced-configuration)\r\n\t- [Remote logging](#remote-logging)\r\n- [Changelog](#changelog)\r\n\r\n## Target Environment\r\n\r\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\r\n\r\n## Simple Example\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"console sink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\n// will log message to console\r\ncleLogger.info(\"log message\");\r\n```\r\n\r\n## Installation\r\n\r\n```bash\r\n$ npm install @prftesp/cle-logger\r\n```\r\n\r\n## Features\r\n\r\n- Runs in Node.js and the browser\r\n- Remote logging\r\n- Multiple log sinks\r\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\r\n- Remote minimum log level overrides\r\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\r\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\r\n- Support for tagging logs with custom component names or correlation tokens\r\n- Send Twilio event hook data to a custom backend\r\n\r\n## Configuration\r\n\r\nTo configure the logger call the ```init``` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\r\n\r\n**Available options**\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// Twilio account sid, required for use with CLE\r\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\r\n\r\n\t// Will be associated with all logs,\r\n\t// if not provided, a unique uuid will be generated\r\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\r\n\t// An array of log sinks that will receive an array of log objects,\r\n\t// and can optionally return a promise\r\n\tlogSinks: [{\r\n\t\t// name of the log sink\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\t// log handler function\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\t// should this log sink accept PII data, defaults to false\r\n\t\tpiiSafe: true,\r\n\t\t// log level for the sink, defaults to INFO\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n\r\n\t// A way to suppress error messages if set to false, defaults to true\r\n\tlogErrorsToConsole: true,\r\n\r\n\t// If logging Twilio Flex Actions, provide a list of actions \r\n\t// to be excluded from logs\r\n\texcludedFlexActionNames: [\"SendTyping\"],\r\n\r\n\t// A custom endpoint to fetch log level overrides\r\n\t// NOTE: the provided url should always end with 'accountSid=',\r\n\t// the library will append the accountSid\r\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\r\n\r\n\t// An instance of ('@twilio/flex-ui').Manager\r\n\ttwilioFlexManager: manager,\r\n\r\n\t// To send data from Twilio event hooks, to a custom backend,\r\n\t// configure one or more endpoints for each event type\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\r\n\t}\r\n});\r\n```\r\n\r\n## API\r\n\r\nThe logger can be imported as an object in CommonJs or ES6 syntax:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n// or\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n```\r\n\r\nAlternatively, all top level members of the ```cleLogger``` are available as named exports and can be imported separately:\r\n\r\n```js\r\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tlogSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\r\n\r\n// vs\r\n\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tcleLogger.logSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\r\n```\r\n\r\n### Log sinks\r\n\r\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the ```logSinks``` config. It's an array of objects containing a ```name```, ```piiSafe``` flag to indicate if the sink can handle PII data without concern for privacy violations, ```logLevel```, and a ```log``` method. The ```log``` method will receive an array of log objects for you to handle. All provided log sinks will be executed in parallel. Log sinks should be async operations, and must return a Promise object. Built-in logger already do this, if you have a custom logger, make sure to return a promise.\r\n\r\nTwo built-in loggers are available out of the box. ```consoleLogger``` for logging to standard console, and ```remoteLogger``` for sending logs over http(s).\r\n\r\nLog sink object:\r\n\r\n``` js\r\n{\r\n\t// name of the log sink\r\n\tname: \"exampleConsoleSink\",\r\n\t// log handler function\r\n\tlog: logs => { \r\n\t\tconsole.log(logs);\r\n\t\treturn Promise.resolve();\r\n\t},\r\n\t// should this log sink accept PII data, defaults to false\r\n\tpiiSafe: true,\r\n\t// log level for the sink, defaults to INFO\r\n\tlogLevel: cleLogger.logLevels.VERBOSE\r\n}\r\n```\r\n\r\nExample log sinks:\r\n\r\n``` js\r\nconst fs = require(\"fs\");\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"fileSink\",\r\n\t\tlog: logs => {\r\n\t\t\tfs.appendFile(\r\n\t\t\t\t\"./log.txt\",\r\n\t\t\t\t`${JSON.stringify(logs)}`,\r\n\t\t\t\tconsole.log\r\n\t\t\t);\r\n\t\t\treturn Promise.resolve();\r\n\t\t}\r\n\t},\r\n\t{\r\n\t\tname: \"remoteSink\",\r\n\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\r\n\t\tlogLevel: cleLogger.logLevels.WARN,\r\n\t\tpiiSafe: true\r\n\t},\r\n\t{\r\n\t\tname: \"consoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}]\r\n});\r\n```\r\n\r\n### Logging levels\r\n\r\nLogging level constants can be found in ```cleLogger.logLevels```.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.logLevels.VERBOSE; // verbose\r\ncleLogger.logLevels.INFO; // info\r\ncleLogger.logLevels.WARN; // warn\r\ncleLogger.logLevels.ERROR; // error\r\ncleLogger.logLevels.CRITICAL; // critical\r\n```\r\n\r\nThere are functions available for each supported logging level:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(...);\r\ncleLogger.info(...);\r\ncleLogger.warn(...);\r\ncleLogger.error(...);\r\ncleLogger.critical(...);\r\n```\r\n\r\nThe functions are all async, return ```Promise<void>```, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\r\n\r\n### Logging parameters\r\n\r\nAll logging functions take the same parameters:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(\r\n\t// The only required property, no logs will be generated without it\r\n\t\"Log message\",\r\n\r\n\t// Any additional log data you would like to include with the log,\r\n\t// can be any data type\r\n\t{ data: \"test\" },\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\t// CLE uses it to index these properties in Application Insights\r\n\t// For CLE, try to use keys found in 'cleLogger.customPropertyKeys'\r\n\t{ userId: 123 },\r\n\r\n\t// Another useful way to 'tag' your logs for easier searching\r\n\t// Can be any string, but for CLE, try to use available options in 'cleLogger.componentNames'\r\n\t// If you would like more component names added, please let us know \r\n\t\"ProfileComponent\",\r\n\r\n\t// Log code, can be any number used to categorize logs,\r\n\t// there will be a separate document listing CLE codes\r\n\t70123,\r\n\r\n\t// Correlation token to be associated with this log only,\r\n\t// if one is not provided, the token supplied/generated during 'init' will be used\r\n\t\"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\t\r\n\t// Does the log contain PII data, defaults to false. If set to true, the log will be \r\n\t// passed in only to those log sinks that have 'piiSafe' set to true.\r\n\ttrue,\r\n);\r\n```\r\n\r\n### Available component names\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.componentNames.SESSION;\r\ncleLogger.componentNames.DIALER;\r\ncleLogger.componentNames.DASHBOARD;\r\ncleLogger.componentNames.AFTERCALL;\r\ncleLogger.componentNames.SUPERVISOR;\r\n```\r\n\r\n### Available custom property keys\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.customPropertyKeys.CHANNEL_SID;\r\ncleLogger.customPropertyKeys.TASK_SID;\r\ncleLogger.customPropertyKeys.CALL_SID;\r\ncleLogger.customPropertyKeys.WORKFLOW_SID;\r\ncleLogger.customPropertyKeys.WORKER_SID;\r\n```\r\n\r\n### Log codes\r\n\r\nTo categorize a log in one of the following categories, provide a log code in the appropriate range:\r\n\r\n- 100 000 - 109 999 => CALL_STATUS\r\n- 110 000 - 119 999 => IVR\r\n- 120 000 - 129 999 => ROUTING\r\n- 130 000 - 139 999 => AGENT_ACTION\r\n- 140 000 - 149 999 => TRANSFER\r\n- 150 000 - 159 999 => RM_INTEGRATION\r\n- 160 000 - 169 999 => WFM_INTEGRATION\r\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\r\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\r\n\r\n### Registering Twilio Actions\r\n\r\nExample in a Twilio Flex UI plugin:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\n/**\r\n * @param flex { typeof import('@twilio/flex-ui') }\r\n * @param manager { import('@twilio/flex-ui').Manager }\r\n */\r\ninit(flex, manager) {\r\n\tcleLogger.init({\r\n\t\taccountSid: \"your-account-sid\"\r\n\t});\r\n\r\n\tcleLogger.registerTwilioActions(flex);\r\n}\r\n```\r\n\r\n**Note**: A Twilio account sid must be provided during ```init``` with the ```accountSid``` parameter, otherwise the actions won't be logged.\r\n\r\nThe logger will loop through all actions found under ```flex.Actions.actions```, and attach ```after``` event handlers to log the action name and payload. Any private properties found in the payload (starting with '_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Any actions names configured in ```excludedFlexActionNames``` will not be included in the logs.\r\n\r\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\r\n\r\n**Twilio Actions log levels**\r\n\r\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of ```INFO```.\r\n\r\n``` js\r\nAcceptTask: INFO\r\nCancelTransfer: INFO\r\nCompleteTask: INFO\r\nHangupCall: INFO\r\nHideDirectory: VERBOSE\r\nHistoryGo: VERBOSE\r\nHistoryGoBack: VERBOSE\r\nHistoryGoForward: VERBOSE\r\nHistoryPush: VERBOSE\r\nHistoryReplace: VERBOSE\r\nHoldCall: INFO\r\nHoldParticipant: INFO\r\nKickParticipant: INFO\r\nLogout: INFO\r\nMonitorCall: INFO\r\nNavigateToView: INFO\r\nRejectTask: INFO\r\nSelectTask: INFO\r\nSelectTaskInSupervisor: INFO\r\nSelectWorkerInSupervisor: INFO\r\nSendMessage: INFO\r\nSendTyping: VERBOSE\r\nSetActivity: INFO\r\nSetInputText: VERBOSE\r\nShowDirectory: VERBOSE\r\nStopMonitoringCall: INFO\r\nToggleMute: INFO\r\nToggleSidebar: VERBOSE\r\nTransferTask: INFO\r\nUnholdCall: INFO\r\nUnholdParticipant: INFO\r\nWrapupTask: INFO\r\n```\r\n\r\n### Twilio Event Hooks\r\n\r\nCurrently, Twilio only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with ```twilioEventHooks``` option, and use the following functions to send event data to a custom endpoint.\r\n\r\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is ```application/x-www-form-urlencoded``` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// unrelated properties omitted\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\r\n\t}\r\n});\r\n\r\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\r\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\r\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\r\n```\r\n\r\nExample Azure Function hook for debugger events:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\nmodule.exports = async function (context, req) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\t}\r\n\t});\r\n\r\n\t// log to console\r\n\tcleLogger.info(\"Received Twilio debugger event\");\r\n\t\r\n\t// send event data to a configured url\r\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\r\n};\r\n```\r\n\r\n**NOTE:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to ```x-www-form-urlencoded``` format.\r\n\r\nExample Twilio function task router relay using the ```form-urlencoded``` npm package to convert the data to ```x-www-form-urlencoded``` format:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\nconst formurlencoded = require('form-urlencoded').default;\r\n\r\nexports.handler = async function(context, event, callback) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\t}\r\n\t});\r\n\r\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\r\n\r\n\tcallback();\r\n};\r\n```\r\n\r\n### Version\r\n\r\nVersion will be automatically included in all logs.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.version; // 1.0.0\r\n```\r\n\r\n## Log Object\r\n\r\n```js\r\n{\r\n\t// Account sid if provided during init\r\n\taccountSid: string;\r\n\r\n\t// Any additional log data to be included with the log\r\n\tadditionalData: unknown;\r\n\t\r\n\t// Component name, if provided\r\n\tcomponentName: string;\r\n\r\n\t// Generated or configured token\r\n\tcorrelationToken: string;\r\n\r\n\t// Current UTC datetime in ISO 8601 format\r\n\teventDate: string;\r\n\r\n\t// Log level\r\n\tlevel: string;\r\n\r\n\t// Log code, if provided\r\n\tlogCode: number;\r\n\r\n\t// Log message\r\n\tmessage: string;\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\tproperties: object;\r\n\r\n\t// BrowserFunction if the environment is the browser\r\n\t// CloudFunction if the environment is Node.js\r\n\tsource: string;\r\n\r\n\t// Logging library version \r\n\tloggerVersion: string;\r\n}\r\n```\r\n\r\n## Handling PII Data\r\n\r\nThe logger provides support for marking logs as containing PII (personally identifiable data), and configuring each log sink to indicate if it can handle PII data. By default, log sinks are not marked as PII safe, meaning they will not receive logs marked as containing PII. Marking a log sink as PII safe, means that the logger should be able to handle PII data without privacy violations (for example, a remote log sink that sends data to an API that masks PII data would be considered safe).\r\n\r\nAdditionally, the logger exports a function ```tagPII``` which can be used to tag parts of a string message as PII for easier masking. Tags can be used by backend systems to mask the data with a simple regex search and replace.\r\n\r\nExample tagged output:\r\n\r\n```js\r\nimport { tagPII } from \"@prftesp/cle-logger\";\r\n\r\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\r\n```\r\n\r\n## Advanced Configuration\r\n\r\n### Remote logging\r\n\r\nThe library provides a way to provide you own remote log url, or to remotely override the min log level. This can be useful for temporarily setting the log level to verbose in a production environment, for quick troubleshooting.\r\n\r\n**Remote log endpoint**\r\n\r\nTo implement your own remote logging endpoint, all you have to do is accept ```POST``` requests, with a JSON payload of an array of [log objects](#log-object), and return either ```{ success: true }``` or ```{ success: false }```. The endpoint should always return a ```200 OK``` status code. A good use-case would be leveraging a serverless api platform to output logs to a queue.\r\n\r\nExample Azure Function:\r\n\r\n```js\r\nmodule.exports = async function (context, req) {\r\n    context.log(req.body); // body will have the log array\r\n\r\n    context.res = {\r\n        status: 200,\r\n        body: { success: true }\r\n    };\r\n};\r\n```\r\n\r\n**Remote log level override**\r\n\r\nThe ```minLogLevelUrl``` configuration property is used to provide an endpoint that accepts ```GET``` requests and a ```accountSid``` query string parameter. ```accountSid``` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the ```minLogLevel``` configuration property. If null is returned, the ```minLogLevel``` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a ```200 OK``` status code and use the ```success``` property to indicate failure.\r\n\r\nExample success response:\r\n\r\n```json\r\n{ \r\n\t\"success\": true,\r\n\t\"minLogLevel\": \"warn\"\r\n}\r\n```\r\n\r\nExample error response:\r\n\r\n```json\r\n{ \r\n\t\"success\": false,\r\n\t\"minLogLevel\": null\r\n}\r\n```\r\n\r\n## Changelog\r\n\r\n### v2.0.4\r\n\r\n**Bug fixes**\r\n- Fixed issue with logging to console in Twilio Functions\r\n\r\n### v2.0.0 [2019-12-20]\r\n\r\n**New features**\r\n- All logging output is now configured using the ```logSinks``` property. The library provides two commonly used loggers - ```consoleLogger``` and a ```remoteLogger``` that accepts the log url as a parameter. [Configuration](#configuration)\r\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\r\n- Log functions have a new optional ```containsPII``` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\r\n- A new helper function ```tagPII``` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\r\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\r\n\r\n**Breaking changes**\r\n- ```customLogSink``` configuration renamed to ```logSinks```\r\n- Removed ```disableRemoteLogging``` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\r\n- Removed ```logToConsole``` configuration. This is now configured as a log sink with a ```consoleLogger``` helper function.\r\n- Removed ```logUrl``` configuration. This is now configured as a log sink with a ```remoteLogger``` helper function.\r\n- Removed ```minLogLevel``` configuration. This is now configured per log sink.\r\n\r\n**Migration guide**\r\n``` javascript\r\nimport cleLogger from \"@prftesp/cle-logger\";\r\n\r\n// v1.0.0 and older config\r\ncleLogger.init({\r\n\tdisableRemoteLogSinks: true, // no direct replacement\r\n\tlogToConsole: true, // replaced by the \"console logger\" sink\r\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\r\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\r\n\tcustomLogSinks: [{\r\n\t\tname: \"custom sink\",\r\n\t\tlog: logs => {/* your custom logging */}\r\n\t}]\r\n});\r\n\r\n// v2.0.0 config\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"custom sink\",\r\n\t\tlog: logs => {/* your custom logging */},\r\n\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t// set to true to get feature parity with older version,\r\n\t\t// please consider your use case before setting\r\n\t\tpiiSafe: true\r\n\t},\r\n\t{\r\n\t\tname: \"console logger\",\r\n\t\tlog: consoleLogger,\r\n\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t// set to true to get feature parity with older version,\r\n\t\t// please consider your use case before setting\r\n\t\tpiiSafe: true\r\n\t},\r\n\t{\r\n\t\tname: \"remote logger\",\r\n\t\tlog: remoteLogger(\"http://log.output/api\"),\r\n\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t// set to true to get feature parity with older version,\r\n\t\t// please consider your use case before setting\r\n\t\tpiiSafe: true\t\t\r\n\t}]\r\n});\r\n```\r\n\r\n### v1.0.0 [2019-09-12]\r\n\r\n- Initial release\r\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.0.4-beta.6","_nodeVersion":"10.15.2","_npmVersion":"6.11.3","dist":{"integrity":"sha512-SsBbJaN8e74oT0gUJisEJ3vL5I6EWn6u+9UKQLfOK7Y0lk3NxiGeD/5+ebttVEok27s7eYwXKcFujkK51Sqmzw==","shasum":"753b7dbd0889c1a775efa326875a38e07a4f21df","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.0.4-beta.6.tgz","fileCount":17,"unpackedSize":67668,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeOJ2wCRA9TVsSAnZWagAA5jQP/A7fS6WyoWI4gvMwnjbD\nU9OEzW+bifkBd+WKax//3d856mxHnUFa9OeOtgthXmFvO+QgQribamKNDOMK\nLhhOCbjX3PSF/bDkW4H4yMU1eJtwALYE1LFTlgLNFKhLZnJsTdNCPo6j2ooz\nd6KlAy81yDSCF8KpQZ0bL6f2fcx8LFCiE/kcqJYGo4n/2Sv6fE2n+rnSypMN\nobkgYZQNUsNFA6A+4CACDmTYnIyerbtne3OzmlgckgNUHNMhBPy0nQY+nUHg\npOLEm0Y9dxdaZQ65CogCRSWih+vfyBt62Vq5cDuwTR6yezRV1fIGlciQKgZ1\n42OxvgBcbiSGih6u8O5AhUbGdR92doGSUQOJzNU1vF+bxYfbUVFNW5VBOgkX\n0uRQW4CRQSxhbJbyMu+ApY6vcJaMlg8SssgHOwGJys85858Rgn+emCUScDsg\n1S6gI2EYPwn/Gc0rVcAXa5Mbfhcch7gP2QicsffS4YT6TiEtYW86nwNpxxZI\nZKZg+MflcrfvHukxmlzk1IW6wer2wv6X5kQOmzRcED8GeJ/unasvILxE6IgR\nQpq2hMuqDK+iuuS+JjX8P1eR2dQpqQwpkHoHDr4MCDjXFlJT5zcJTCA95EQB\nmjdTvGTH9fR7a876LCehJKhJvNe5l4cofnR7trDBsv1DJ31/s8VY5ll0F0R/\nNdz6\r\n=hpSM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICGM+bbri863MsNnf1UGXtSxAsHUxt4g55t6gr8zu8F4AiB9lc1MLOwqLaYv+CqcUkOlyrJfLe0I0Z3XSSqNpsVWZg=="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.0.4-beta.6_1580768687641_0.8226851843973184"},"_hasShrinkwrap":false},"2.0.4-beta.7":{"name":"@prftesp/cle-logger","version":"2.0.4-beta.7","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿CLE Logger\r\n===\r\n\r\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\r\n\r\n## Documentation\r\n\r\n- [Target Environment](#target-environment)\r\n- [Simple Example](#simple-example)\r\n- [Installation](#installation)\r\n- [Features](#features)\r\n- [Configuration](#configuration)\r\n- [API](#api)\r\n\t- [Log sinks](#log-sinks)\r\n\t- [Logging levels](#logging-levels)\r\n\t- [Logging parameters](#logging-parameters)\r\n\t- [Available component names](#available-component-names)\r\n\t- [Available custom property keys](#available-custom-property-keys)\r\n\t- [Log codes](#log-codes)\r\n\t- [Registering Twilio Actions](#registering-twilio-actions)\r\n\t- [Twilio Event Hooks](#twilio-event-hooks)\r\n\t- [Version](#version)\r\n- [Log Object](#log-object)\r\n- [Handling PII Data](#handling-pii-data)\r\n- [Advanced Configuration](#advanced-configuration)\r\n\t- [Remote logging](#remote-logging)\r\n- [Changelog](#changelog)\r\n\r\n## Target Environment\r\n\r\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\r\n\r\n## Simple Example\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"console sink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\n// will log message to console\r\ncleLogger.info(\"log message\");\r\n```\r\n\r\n## Installation\r\n\r\n```bash\r\n$ npm install @prftesp/cle-logger\r\n```\r\n\r\n## Features\r\n\r\n- Runs in Node.js and the browser\r\n- Remote logging\r\n- Multiple log sinks\r\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\r\n- Remote minimum log level overrides\r\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\r\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\r\n- Support for tagging logs with custom component names or correlation tokens\r\n- Send Twilio event hook data to a custom backend\r\n\r\n## Configuration\r\n\r\nTo configure the logger call the ```init``` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\r\n\r\n**Available options**\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// Twilio account sid, required for use with CLE\r\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\r\n\r\n\t// Will be associated with all logs,\r\n\t// if not provided, a unique uuid will be generated\r\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\r\n\t// An array of log sinks that will receive an array of log objects,\r\n\t// and can optionally return a promise\r\n\tlogSinks: [{\r\n\t\t// name of the log sink\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\t// log handler function\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\t// should this log sink accept PII data, defaults to false\r\n\t\tpiiSafe: true,\r\n\t\t// log level for the sink, defaults to INFO\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n\r\n\t// A way to suppress error messages if set to false, defaults to true\r\n\tlogErrorsToConsole: true,\r\n\r\n\t// If logging Twilio Flex Actions, provide a list of actions \r\n\t// to be excluded from logs\r\n\texcludedFlexActionNames: [\"SendTyping\"],\r\n\r\n\t// A custom endpoint to fetch log level overrides\r\n\t// NOTE: the provided url should always end with 'accountSid=',\r\n\t// the library will append the accountSid\r\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\r\n\r\n\t// An instance of ('@twilio/flex-ui').Manager\r\n\ttwilioFlexManager: manager,\r\n\r\n\t// To send data from Twilio event hooks, to a custom backend,\r\n\t// configure one or more endpoints for each event type\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\r\n\t}\r\n});\r\n```\r\n\r\n## API\r\n\r\nThe logger can be imported as an object in CommonJs or ES6 syntax:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n// or\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n```\r\n\r\nAlternatively, all top level members of the ```cleLogger``` are available as named exports and can be imported separately:\r\n\r\n```js\r\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tlogSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\r\n\r\n// vs\r\n\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tcleLogger.logSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\r\n```\r\n\r\n### Log sinks\r\n\r\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the ```logSinks``` config. It's an array of objects containing a ```name```, ```piiSafe``` flag to indicate if the sink can handle PII data without concern for privacy violations, ```logLevel```, and a ```log``` method. The ```log``` method will receive an array of log objects for you to handle. All provided log sinks will be executed in parallel. Log sinks should be async operations, and must return a Promise object. Built-in logger already do this, if you have a custom logger, make sure to return a promise.\r\n\r\nTwo built-in loggers are available out of the box. ```consoleLogger``` for logging to standard console, and ```remoteLogger``` for sending logs over http(s).\r\n\r\nLog sink object:\r\n\r\n``` js\r\n{\r\n\t// name of the log sink\r\n\tname: \"exampleConsoleSink\",\r\n\t// log handler function\r\n\tlog: logs => { \r\n\t\tconsole.log(logs);\r\n\t\treturn Promise.resolve();\r\n\t},\r\n\t// should this log sink accept PII data, defaults to false\r\n\tpiiSafe: true,\r\n\t// log level for the sink, defaults to INFO\r\n\tlogLevel: cleLogger.logLevels.VERBOSE\r\n}\r\n```\r\n\r\nExample log sinks:\r\n\r\n``` js\r\nconst fs = require(\"fs\");\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"fileSink\",\r\n\t\tlog: logs => {\r\n\t\t\tfs.appendFile(\r\n\t\t\t\t\"./log.txt\",\r\n\t\t\t\t`${JSON.stringify(logs)}`,\r\n\t\t\t\tconsole.log\r\n\t\t\t);\r\n\t\t\treturn Promise.resolve();\r\n\t\t}\r\n\t},\r\n\t{\r\n\t\tname: \"remoteSink\",\r\n\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\r\n\t\tlogLevel: cleLogger.logLevels.WARN,\r\n\t\tpiiSafe: true\r\n\t},\r\n\t{\r\n\t\tname: \"consoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}]\r\n});\r\n```\r\n\r\n### Logging levels\r\n\r\nLogging level constants can be found in ```cleLogger.logLevels```.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.logLevels.VERBOSE; // verbose\r\ncleLogger.logLevels.INFO; // info\r\ncleLogger.logLevels.WARN; // warn\r\ncleLogger.logLevels.ERROR; // error\r\ncleLogger.logLevels.CRITICAL; // critical\r\n```\r\n\r\nThere are functions available for each supported logging level:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(...);\r\ncleLogger.info(...);\r\ncleLogger.warn(...);\r\ncleLogger.error(...);\r\ncleLogger.critical(...);\r\n```\r\n\r\nThe functions are all async, return ```Promise<void>```, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\r\n\r\n### Logging parameters\r\n\r\nAll logging functions take the same parameters:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(\r\n\t// The only required property, no logs will be generated without it\r\n\t\"Log message\",\r\n\r\n\t// Any additional log data you would like to include with the log,\r\n\t// can be any data type\r\n\t{ data: \"test\" },\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\t// CLE uses it to index these properties in Application Insights\r\n\t// For CLE, try to use keys found in 'cleLogger.customPropertyKeys'\r\n\t{ userId: 123 },\r\n\r\n\t// Another useful way to 'tag' your logs for easier searching\r\n\t// Can be any string, but for CLE, try to use available options in 'cleLogger.componentNames'\r\n\t// If you would like more component names added, please let us know \r\n\t\"ProfileComponent\",\r\n\r\n\t// Log code, can be any number used to categorize logs,\r\n\t// there will be a separate document listing CLE codes\r\n\t70123,\r\n\r\n\t// Correlation token to be associated with this log only,\r\n\t// if one is not provided, the token supplied/generated during 'init' will be used\r\n\t\"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\t\r\n\t// Does the log contain PII data, defaults to false. If set to true, the log will be \r\n\t// passed in only to those log sinks that have 'piiSafe' set to true.\r\n\ttrue,\r\n);\r\n```\r\n\r\n### Available component names\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.componentNames.SESSION;\r\ncleLogger.componentNames.DIALER;\r\ncleLogger.componentNames.DASHBOARD;\r\ncleLogger.componentNames.AFTERCALL;\r\ncleLogger.componentNames.SUPERVISOR;\r\n```\r\n\r\n### Available custom property keys\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.customPropertyKeys.CHANNEL_SID;\r\ncleLogger.customPropertyKeys.TASK_SID;\r\ncleLogger.customPropertyKeys.CALL_SID;\r\ncleLogger.customPropertyKeys.WORKFLOW_SID;\r\ncleLogger.customPropertyKeys.WORKER_SID;\r\n```\r\n\r\n### Log codes\r\n\r\nTo categorize a log in one of the following categories, provide a log code in the appropriate range:\r\n\r\n- 100 000 - 109 999 => CALL_STATUS\r\n- 110 000 - 119 999 => IVR\r\n- 120 000 - 129 999 => ROUTING\r\n- 130 000 - 139 999 => AGENT_ACTION\r\n- 140 000 - 149 999 => TRANSFER\r\n- 150 000 - 159 999 => RM_INTEGRATION\r\n- 160 000 - 169 999 => WFM_INTEGRATION\r\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\r\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\r\n\r\n### Registering Twilio Actions\r\n\r\nExample in a Twilio Flex UI plugin:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\n/**\r\n * @param flex { typeof import('@twilio/flex-ui') }\r\n * @param manager { import('@twilio/flex-ui').Manager }\r\n */\r\ninit(flex, manager) {\r\n\tcleLogger.init({\r\n\t\taccountSid: \"your-account-sid\"\r\n\t});\r\n\r\n\tcleLogger.registerTwilioActions(flex);\r\n}\r\n```\r\n\r\n**Note**: A Twilio account sid must be provided during ```init``` with the ```accountSid``` parameter, otherwise the actions won't be logged.\r\n\r\nThe logger will loop through all actions found under ```flex.Actions.actions```, and attach ```after``` event handlers to log the action name and payload. Any private properties found in the payload (starting with '_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Any actions names configured in ```excludedFlexActionNames``` will not be included in the logs.\r\n\r\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\r\n\r\n**Twilio Actions log levels**\r\n\r\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of ```INFO```.\r\n\r\n``` js\r\nAcceptTask: INFO\r\nCancelTransfer: INFO\r\nCompleteTask: INFO\r\nHangupCall: INFO\r\nHideDirectory: VERBOSE\r\nHistoryGo: VERBOSE\r\nHistoryGoBack: VERBOSE\r\nHistoryGoForward: VERBOSE\r\nHistoryPush: VERBOSE\r\nHistoryReplace: VERBOSE\r\nHoldCall: INFO\r\nHoldParticipant: INFO\r\nKickParticipant: INFO\r\nLogout: INFO\r\nMonitorCall: INFO\r\nNavigateToView: INFO\r\nRejectTask: INFO\r\nSelectTask: INFO\r\nSelectTaskInSupervisor: INFO\r\nSelectWorkerInSupervisor: INFO\r\nSendMessage: INFO\r\nSendTyping: VERBOSE\r\nSetActivity: INFO\r\nSetInputText: VERBOSE\r\nShowDirectory: VERBOSE\r\nStopMonitoringCall: INFO\r\nToggleMute: INFO\r\nToggleSidebar: VERBOSE\r\nTransferTask: INFO\r\nUnholdCall: INFO\r\nUnholdParticipant: INFO\r\nWrapupTask: INFO\r\n```\r\n\r\n### Twilio Event Hooks\r\n\r\nCurrently, Twilio only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with ```twilioEventHooks``` option, and use the following functions to send event data to a custom endpoint.\r\n\r\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is ```application/x-www-form-urlencoded``` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// unrelated properties omitted\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\r\n\t}\r\n});\r\n\r\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\r\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\r\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\r\n```\r\n\r\nExample Azure Function hook for debugger events:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\nmodule.exports = async function (context, req) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\r\n\t\t}\r\n\t});\r\n\r\n\t// log to console\r\n\tcleLogger.info(\"Received Twilio debugger event\");\r\n\t\r\n\t// send event data to a configured url\r\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\r\n};\r\n```\r\n\r\n**NOTE:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to ```x-www-form-urlencoded``` format.\r\n\r\nExample Twilio function task router relay using the ```form-urlencoded``` npm package to convert the data to ```x-www-form-urlencoded``` format:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\nconst formurlencoded = require('form-urlencoded').default;\r\n\r\nexports.handler = async function(context, event, callback) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\r\n\t\t}\r\n\t});\r\n\r\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\r\n\r\n\tcallback();\r\n};\r\n```\r\n\r\n### Version\r\n\r\nVersion will be automatically included in all logs.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.version; // 1.0.0\r\n```\r\n\r\n## Log Object\r\n\r\n```js\r\n{\r\n\t// Account sid if provided during init\r\n\taccountSid: string;\r\n\r\n\t// Any additional log data to be included with the log\r\n\tadditionalData: unknown;\r\n\t\r\n\t// Component name, if provided\r\n\tcomponentName: string;\r\n\r\n\t// Generated or configured token\r\n\tcorrelationToken: string;\r\n\r\n\t// Current UTC datetime in ISO 8601 format\r\n\teventDate: string;\r\n\r\n\t// Log level\r\n\tlevel: string;\r\n\r\n\t// Log code, if provided\r\n\tlogCode: number;\r\n\r\n\t// Log message\r\n\tmessage: string;\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\tproperties: object;\r\n\r\n\t// BrowserFunction if the environment is the browser\r\n\t// CloudFunction if the environment is Node.js\r\n\tsource: string;\r\n\r\n\t// Logging library version \r\n\tloggerVersion: string;\r\n}\r\n```\r\n\r\n## Handling PII Data\r\n\r\nThe logger provides support for marking logs as containing PII (personally identifiable data), and configuring each log sink to indicate if it can handle PII data. By default, log sinks are not marked as PII safe, meaning they will not receive logs marked as containing PII. Marking a log sink as PII safe, means that the logger should be able to handle PII data without privacy violations (for example, a remote log sink that sends data to an API that masks PII data would be considered safe).\r\n\r\nAdditionally, the logger exports a function ```tagPII``` which can be used to tag parts of a string message as PII for easier masking. Tags can be used by backend systems to mask the data with a simple regex search and replace.\r\n\r\nExample tagged output:\r\n\r\n```js\r\nimport { tagPII } from \"@prftesp/cle-logger\";\r\n\r\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\r\n```\r\n\r\n## Advanced Configuration\r\n\r\n### Remote logging\r\n\r\nThe library provides a way to provide you own remote log url, or to remotely override the min log level. This can be useful for temporarily setting the log level to verbose in a production environment, for quick troubleshooting.\r\n\r\n**Remote log endpoint**\r\n\r\nTo implement your own remote logging endpoint, all you have to do is accept ```POST``` requests, with a JSON payload of an array of [log objects](#log-object), and return either ```{ success: true }``` or ```{ success: false }```. The endpoint should always return a ```200 OK``` status code. A good use-case would be leveraging a serverless api platform to output logs to a queue.\r\n\r\nExample Azure Function:\r\n\r\n```js\r\nmodule.exports = async function (context, req) {\r\n    context.log(req.body); // body will have the log array\r\n\r\n    context.res = {\r\n        status: 200,\r\n        body: { success: true }\r\n    };\r\n};\r\n```\r\n\r\n**Remote log level override**\r\n\r\nThe ```minLogLevelUrl``` configuration property is used to provide an endpoint that accepts ```GET``` requests and a ```accountSid``` query string parameter. ```accountSid``` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the ```minLogLevel``` configuration property. If null is returned, the ```minLogLevel``` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a ```200 OK``` status code and use the ```success``` property to indicate failure.\r\n\r\nExample success response:\r\n\r\n```json\r\n{ \r\n\t\"success\": true,\r\n\t\"minLogLevel\": \"warn\"\r\n}\r\n```\r\n\r\nExample error response:\r\n\r\n```json\r\n{ \r\n\t\"success\": false,\r\n\t\"minLogLevel\": null\r\n}\r\n```\r\n\r\n## Changelog\r\n\r\n### v2.0.4\r\n\r\n**Bug fixes**\r\n- Fixed issue with logging to console in Twilio Functions\r\n\r\n### v2.0.0 [2019-12-20]\r\n\r\n**New features**\r\n- All logging output is now configured using the ```logSinks``` property. The library provides two commonly used loggers - ```consoleLogger``` and a ```remoteLogger``` that accepts the log url as a parameter. [Configuration](#configuration)\r\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\r\n- Log functions have a new optional ```containsPII``` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\r\n- A new helper function ```tagPII``` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\r\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\r\n\r\n**Breaking changes**\r\n- ```customLogSink``` configuration renamed to ```logSinks```\r\n- Removed ```disableRemoteLogging``` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\r\n- Removed ```logToConsole``` configuration. This is now configured as a log sink with a ```consoleLogger``` helper function.\r\n- Removed ```logUrl``` configuration. This is now configured as a log sink with a ```remoteLogger``` helper function.\r\n- Removed ```minLogLevel``` configuration. This is now configured per log sink.\r\n\r\n**Migration guide**\r\n``` javascript\r\nimport cleLogger from \"@prftesp/cle-logger\";\r\n\r\n// v1.0.0 and older config\r\ncleLogger.init({\r\n\tdisableRemoteLogSinks: true, // no direct replacement\r\n\tlogToConsole: true, // replaced by the \"console logger\" sink\r\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\r\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\r\n\tcustomLogSinks: [{\r\n\t\tname: \"custom sink\",\r\n\t\tlog: logs => {/* your custom logging */}\r\n\t}]\r\n});\r\n\r\n// v2.0.0 config\r\ncleLogger.init({\r\n\tlogSinks: [{\r\n\t\tname: \"custom sink\",\r\n\t\tlog: logs => {/* your custom logging */},\r\n\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t// set to true to get feature parity with older version,\r\n\t\t// please consider your use case before setting\r\n\t\tpiiSafe: true\r\n\t},\r\n\t{\r\n\t\tname: \"console logger\",\r\n\t\tlog: consoleLogger,\r\n\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t// set to true to get feature parity with older version,\r\n\t\t// please consider your use case before setting\r\n\t\tpiiSafe: true\r\n\t},\r\n\t{\r\n\t\tname: \"remote logger\",\r\n\t\tlog: remoteLogger(\"http://log.output/api\"),\r\n\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t// set to true to get feature parity with older version,\r\n\t\t// please consider your use case before setting\r\n\t\tpiiSafe: true\t\t\r\n\t}]\r\n});\r\n```\r\n\r\n### v1.0.0 [2019-09-12]\r\n\r\n- Initial release\r\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.0.4-beta.7","_nodeVersion":"10.15.2","_npmVersion":"6.11.3","dist":{"integrity":"sha512-R4saywrSiPlXqgShqadKgGn0k/lihKqTVagLyopNGC2ENswC6yOCqfqmqC/k71G39vbO6ifbD5c95Hl9dtqMEA==","shasum":"44e369e3566f5e1c12ec12c8916260c4c5fc7c6c","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.0.4-beta.7.tgz","fileCount":17,"unpackedSize":67831,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeOJ4JCRA9TVsSAnZWagAA+FcP/0JI2Fqj1h5Pf3g/8488\nNuMbw9fTpEHBivf/x4XmJzgxgt7WOyZvO+Dvj1f2wZ7gn2gcmvPq+0p1MX2h\nL47zd0+eVZeZ2ilH+7K5vGEIMFYuHVRZiEZhQA2WHar+HlwPOxjMxjLVXFUT\n4hn6bVGgqzIJJA4YogqRbXPQJCbnVrc8lcxM0LkMEbXTtmEO+bsdPx6fR9AU\nnpZByZnCsQnOoMjkvJ/0/7CZ6UcYeocvPaYD1kY9KJ9dWgY5dfVdoVZ/h38R\nyEvMA7RnqM6LGhuEyxpshpX1xD6eOrxBlGCSB8oIuYqcSCnqdy195HB/gJ22\n+ljU0TvGSRB5FFeGPMTItyRTe22rg9bOB+Q2any/UWW9haRUZ78hvH22cLzW\niZ5ZfUMACsGADccDRID2FFObA0js2nfA4SUjq/2ZIgoA9raqUjrGBYs8Cq+2\nOF9++7fUhAeVBbNU0nFUVZGM5o0aawJCQBIOgX0zornuUdIKT0cNl15TEpKk\net5yXxJ6JF7nNMF1l1FRi3dltJZq5HeFe3BrSBPA4lF69s7sAXkKXY3EbfBK\nB/J0xc7tT/bywfw7R4gEKrkHoPzB1DDXN7mViX11qVAPwlarW05mHOhzuMhe\nB8/ATkxm9B92Fe+AhGd0Y6Gf1GSsprpxZSceoau2qm/YCYALrIsabzcPGxLe\ns+DS\r\n=Ef2B\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCOR74WTEyqlw4HGZE9NXGVeVrmgVEyktX2OyVyxsoB/QIhAP9RbZL1z2Cr2rMZCwtgthJA+wZ5pNNlyvXK/8j3k9M5"}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.0.4-beta.7_1580768776731_0.1559673601973237"},"_hasShrinkwrap":false},"2.0.4-beta.20200203.3":{"name":"@prftesp/cle-logger","version":"2.0.4-beta.20200203.3","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿CLE Logger\n===\n\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\n\n## Documentation\n\n- [Target Environment](#target-environment)\n- [Simple Example](#simple-example)\n- [Installation](#installation)\n- [Features](#features)\n- [Configuration](#configuration)\n- [API](#api)\n\t- [Log sinks](#log-sinks)\n\t- [Logging levels](#logging-levels)\n\t- [Logging parameters](#logging-parameters)\n\t- [Available component names](#available-component-names)\n\t- [Available custom property keys](#available-custom-property-keys)\n\t- [Log codes](#log-codes)\n\t- [Registering Twilio Actions](#registering-twilio-actions)\n\t- [Twilio Event Hooks](#twilio-event-hooks)\n\t- [Version](#version)\n- [Log Object](#log-object)\n- [Handling PII Data](#handling-pii-data)\n- [Advanced Configuration](#advanced-configuration)\n\t- [Remote logging](#remote-logging)\n- [Changelog](#changelog)\n\n## Target Environment\n\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\n\n## Simple Example\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\tlogSinks: [{\n\t\tname: \"console sink\",\n\t\tlog: cleLogger.consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}],\n});\n\n// will log message to console\ncleLogger.info(\"log message\");\n```\n\n## Installation\n\n```bash\n$ npm install @prftesp/cle-logger\n```\n\n## Features\n\n- Runs in Node.js and the browser\n- Remote logging\n- Multiple log sinks\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\n- Remote minimum log level overrides\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\n- Support for tagging logs with custom component names or correlation tokens\n- Send Twilio event hook data to a custom backend\n\n## Configuration\n\nTo configure the logger call the ```init``` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\n\n**Available options**\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// Twilio account sid, required for use with CLE\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\n\n\t// Will be associated with all logs,\n\t// if not provided, a unique uuid will be generated\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\n\n\t// An array of log sinks that will receive an array of log objects,\n\t// and can optionally return a promise\n\tlogSinks: [{\n\t\t// name of the log sink\n\t\tname: \"exampleConsoleSink\",\n\t\t// log handler function\n\t\tlog: cleLogger.consoleLogger,\n\t\t// should this log sink accept PII data, defaults to false\n\t\tpiiSafe: true,\n\t\t// log level for the sink, defaults to INFO\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}],\n\n\t// A way to suppress error messages if set to false, defaults to true\n\tlogErrorsToConsole: true,\n\n\t// If logging Twilio Flex Actions, provide a list of actions \n\t// to be excluded from logs\n\texcludedFlexActionNames: [\"SendTyping\"],\n\n\t// A custom endpoint to fetch log level overrides\n\t// NOTE: the provided url should always end with 'accountSid=',\n\t// the library will append the accountSid\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\n\n\t// An instance of ('@twilio/flex-ui').Manager\n\ttwilioFlexManager: manager,\n\n\t// To send data from Twilio event hooks, to a custom backend,\n\t// configure one or more endpoints for each event type\n\ttwilioEventHooks: {\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\n\t}\n});\n```\n\n## API\n\nThe logger can be imported as an object in CommonJs or ES6 syntax:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n// or\nconst cleLogger = require(\"@prftesp/cle-logger\");\n```\n\nAlternatively, all top level members of the ```cleLogger``` are available as named exports and can be imported separately:\n\n```js\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\n\ninit({\n\tlogSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: logLevels.VERBOSE\n\t}],\n});\n\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\n\n// vs\n\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ninit({\n\tcleLogger.logSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: cleLogger.consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}],\n});\n\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\n```\n\n### Log sinks\n\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the ```logSinks``` config. It's an array of objects containing a ```name```, ```piiSafe``` flag to indicate if the sink can handle PII data without concern for privacy violations, ```logLevel```, and a ```log``` method. The ```log``` method will receive an array of log objects for you to handle. All provided log sinks will be executed in parallel. Log sinks should be async operations, and must return a Promise object. Built-in logger already do this, if you have a custom logger, make sure to return a promise.\n\nTwo built-in loggers are available out of the box. ```consoleLogger``` for logging to standard console, and ```remoteLogger``` for sending logs over http(s).\n\nLog sink object:\n\n``` js\n{\n\t// name of the log sink\n\tname: \"exampleConsoleSink\",\n\t// log handler function\n\tlog: logs => { \n\t\tconsole.log(logs);\n\t\treturn Promise.resolve();\n\t},\n\t// should this log sink accept PII data, defaults to false\n\tpiiSafe: true,\n\t// log level for the sink, defaults to INFO\n\tlogLevel: cleLogger.logLevels.VERBOSE\n}\n```\n\nExample log sinks:\n\n``` js\nconst fs = require(\"fs\");\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\ncleLogger.init({\n\tlogSinks: [{\n\t\tname: \"fileSink\",\n\t\tlog: logs => {\n\t\t\tfs.appendFile(\n\t\t\t\t\"./log.txt\",\n\t\t\t\t`${JSON.stringify(logs)}`,\n\t\t\t\tconsole.log\n\t\t\t);\n\t\t\treturn Promise.resolve();\n\t\t}\n\t},\n\t{\n\t\tname: \"remoteSink\",\n\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\n\t\tlogLevel: cleLogger.logLevels.WARN,\n\t\tpiiSafe: true\n\t},\n\t{\n\t\tname: \"consoleSink\",\n\t\tlog: cleLogger.consoleLogger,\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}]\n});\n```\n\n### Logging levels\n\nLogging level constants can be found in ```cleLogger.logLevels```.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.logLevels.VERBOSE; // verbose\ncleLogger.logLevels.INFO; // info\ncleLogger.logLevels.WARN; // warn\ncleLogger.logLevels.ERROR; // error\ncleLogger.logLevels.CRITICAL; // critical\n```\n\nThere are functions available for each supported logging level:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(...);\ncleLogger.info(...);\ncleLogger.warn(...);\ncleLogger.error(...);\ncleLogger.critical(...);\n```\n\nThe functions are all async, return ```Promise<void>```, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\n\n### Logging parameters\n\nAll logging functions take the same parameters:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(\n\t// The only required property, no logs will be generated without it\n\t\"Log message\",\n\n\t// Any additional log data you would like to include with the log,\n\t// can be any data type\n\t{ data: \"test\" },\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\t// CLE uses it to index these properties in Application Insights\n\t// For CLE, try to use keys found in 'cleLogger.customPropertyKeys'\n\t{ userId: 123 },\n\n\t// Another useful way to 'tag' your logs for easier searching\n\t// Can be any string, but for CLE, try to use available options in 'cleLogger.componentNames'\n\t// If you would like more component names added, please let us know \n\t\"ProfileComponent\",\n\n\t// Log code, can be any number used to categorize logs,\n\t// there will be a separate document listing CLE codes\n\t70123,\n\n\t// Correlation token to be associated with this log only,\n\t// if one is not provided, the token supplied/generated during 'init' will be used\n\t\"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\n\t\n\t// Does the log contain PII data, defaults to false. If set to true, the log will be \n\t// passed in only to those log sinks that have 'piiSafe' set to true.\n\ttrue,\n);\n```\n\n### Available component names\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.componentNames.SESSION;\ncleLogger.componentNames.DIALER;\ncleLogger.componentNames.DASHBOARD;\ncleLogger.componentNames.AFTERCALL;\ncleLogger.componentNames.SUPERVISOR;\n```\n\n### Available custom property keys\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.customPropertyKeys.CHANNEL_SID;\ncleLogger.customPropertyKeys.TASK_SID;\ncleLogger.customPropertyKeys.CALL_SID;\ncleLogger.customPropertyKeys.WORKFLOW_SID;\ncleLogger.customPropertyKeys.WORKER_SID;\n```\n\n### Log codes\n\nTo categorize a log in one of the following categories, provide a log code in the appropriate range:\n\n- 100 000 - 109 999 => CALL_STATUS\n- 110 000 - 119 999 => IVR\n- 120 000 - 129 999 => ROUTING\n- 130 000 - 139 999 => AGENT_ACTION\n- 140 000 - 149 999 => TRANSFER\n- 150 000 - 159 999 => RM_INTEGRATION\n- 160 000 - 169 999 => WFM_INTEGRATION\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\n\n### Registering Twilio Actions\n\nExample in a Twilio Flex UI plugin:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n/**\n * @param flex { typeof import('@twilio/flex-ui') }\n * @param manager { import('@twilio/flex-ui').Manager }\n */\ninit(flex, manager) {\n\tcleLogger.init({\n\t\taccountSid: \"your-account-sid\"\n\t});\n\n\tcleLogger.registerTwilioActions(flex);\n}\n```\n\n**Note**: A Twilio account sid must be provided during ```init``` with the ```accountSid``` parameter, otherwise the actions won't be logged.\n\nThe logger will loop through all actions found under ```flex.Actions.actions```, and attach ```after``` event handlers to log the action name and payload. Any private properties found in the payload (starting with '_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Any actions names configured in ```excludedFlexActionNames``` will not be included in the logs.\n\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\n\n**Twilio Actions log levels**\n\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of ```INFO```.\n\n``` js\nAcceptTask: INFO\nCancelTransfer: INFO\nCompleteTask: INFO\nHangupCall: INFO\nHideDirectory: VERBOSE\nHistoryGo: VERBOSE\nHistoryGoBack: VERBOSE\nHistoryGoForward: VERBOSE\nHistoryPush: VERBOSE\nHistoryReplace: VERBOSE\nHoldCall: INFO\nHoldParticipant: INFO\nKickParticipant: INFO\nLogout: INFO\nMonitorCall: INFO\nNavigateToView: INFO\nRejectTask: INFO\nSelectTask: INFO\nSelectTaskInSupervisor: INFO\nSelectWorkerInSupervisor: INFO\nSendMessage: INFO\nSendTyping: VERBOSE\nSetActivity: INFO\nSetInputText: VERBOSE\nShowDirectory: VERBOSE\nStopMonitoringCall: INFO\nToggleMute: INFO\nToggleSidebar: VERBOSE\nTransferTask: INFO\nUnholdCall: INFO\nUnholdParticipant: INFO\nWrapupTask: INFO\n```\n\n### Twilio Event Hooks\n\nCurrently, Twilio only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with ```twilioEventHooks``` option, and use the following functions to send event data to a custom endpoint.\n\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is ```application/x-www-form-urlencoded``` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// unrelated properties omitted\n\ttwilioEventHooks: {\n\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\n\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\n\t\tcallStatus: ['https://example.com/twilio-call-status-hook?key=123']\n\t}\n});\n\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\n```\n\nExample Azure Function hook for debugger events:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\nmodule.exports = async function (context, req) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\tdebugger: ['https://example.com/twilio-debugger-hook?key=123'],\n\t\t}\n\t});\n\n\t// log to console\n\tcleLogger.info(\"Received Twilio debugger event\");\n\t\n\t// send event data to a configured url\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\n};\n```\n\n**NOTE:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to ```x-www-form-urlencoded``` format.\n\nExample Twilio function task router relay using the ```form-urlencoded``` npm package to convert the data to ```x-www-form-urlencoded``` format:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\nconst formurlencoded = require('form-urlencoded').default;\n\nexports.handler = async function(context, event, callback) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\ttaskRouter: ['https://example.com/twilio-task-router-hook?key=123'],\n\t\t}\n\t});\n\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\n\n\tcallback();\n};\n```\n\n### Version\n\nVersion will be automatically included in all logs.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.version; // 1.0.0\n```\n\n## Log Object\n\n```js\n{\n\t// Account sid if provided during init\n\taccountSid: string;\n\n\t// Any additional log data to be included with the log\n\tadditionalData: unknown;\n\t\n\t// Component name, if provided\n\tcomponentName: string;\n\n\t// Generated or configured token\n\tcorrelationToken: string;\n\n\t// Current UTC datetime in ISO 8601 format\n\teventDate: string;\n\n\t// Log level\n\tlevel: string;\n\n\t// Log code, if provided\n\tlogCode: number;\n\n\t// Log message\n\tmessage: string;\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\tproperties: object;\n\n\t// BrowserFunction if the environment is the browser\n\t// CloudFunction if the environment is Node.js\n\tsource: string;\n\n\t// Logging library version \n\tloggerVersion: string;\n}\n```\n\n## Handling PII Data\n\nThe logger provides support for marking logs as containing PII (personally identifiable data), and configuring each log sink to indicate if it can handle PII data. By default, log sinks are not marked as PII safe, meaning they will not receive logs marked as containing PII. Marking a log sink as PII safe, means that the logger should be able to handle PII data without privacy violations (for example, a remote log sink that sends data to an API that masks PII data would be considered safe).\n\nAdditionally, the logger exports a function ```tagPII``` which can be used to tag parts of a string message as PII for easier masking. Tags can be used by backend systems to mask the data with a simple regex search and replace.\n\nExample tagged output:\n\n```js\nimport { tagPII } from \"@prftesp/cle-logger\";\n\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\n```\n\n## Advanced Configuration\n\n### Remote logging\n\nThe library provides a way to provide you own remote log url, or to remotely override the min log level. This can be useful for temporarily setting the log level to verbose in a production environment, for quick troubleshooting.\n\n**Remote log endpoint**\n\nTo implement your own remote logging endpoint, all you have to do is accept ```POST``` requests, with a JSON payload of an array of [log objects](#log-object), and return either ```{ success: true }``` or ```{ success: false }```. The endpoint should always return a ```200 OK``` status code. A good use-case would be leveraging a serverless api platform to output logs to a queue.\n\nExample Azure Function:\n\n```js\nmodule.exports = async function (context, req) {\n    context.log(req.body); // body will have the log array\n\n    context.res = {\n        status: 200,\n        body: { success: true }\n    };\n};\n```\n\n**Remote log level override**\n\nThe ```minLogLevelUrl``` configuration property is used to provide an endpoint that accepts ```GET``` requests and a ```accountSid``` query string parameter. ```accountSid``` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the ```minLogLevel``` configuration property. If null is returned, the ```minLogLevel``` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a ```200 OK``` status code and use the ```success``` property to indicate failure.\n\nExample success response:\n\n```json\n{ \n\t\"success\": true,\n\t\"minLogLevel\": \"warn\"\n}\n```\n\nExample error response:\n\n```json\n{ \n\t\"success\": false,\n\t\"minLogLevel\": null\n}\n```\n\n## Changelog\n\n### v2.0.4\n\n**Bug fixes**\n- Fixed issue with logging to console in Twilio Functions\n\n### v2.0.0 [2019-12-20]\n\n**New features**\n- All logging output is now configured using the ```logSinks``` property. The library provides two commonly used loggers - ```consoleLogger``` and a ```remoteLogger``` that accepts the log url as a parameter. [Configuration](#configuration)\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\n- Log functions have a new optional ```containsPII``` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\n- A new helper function ```tagPII``` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\n\n**Breaking changes**\n- ```customLogSink``` configuration renamed to ```logSinks```\n- Removed ```disableRemoteLogging``` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\n- Removed ```logToConsole``` configuration. This is now configured as a log sink with a ```consoleLogger``` helper function.\n- Removed ```logUrl``` configuration. This is now configured as a log sink with a ```remoteLogger``` helper function.\n- Removed ```minLogLevel``` configuration. This is now configured per log sink.\n\n**Migration guide**\n``` javascript\nimport cleLogger from \"@prftesp/cle-logger\";\n\n// v1.0.0 and older config\ncleLogger.init({\n\tdisableRemoteLogSinks: true, // no direct replacement\n\tlogToConsole: true, // replaced by the \"console logger\" sink\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\n\tcustomLogSinks: [{\n\t\tname: \"custom sink\",\n\t\tlog: logs => {/* your custom logging */}\n\t}]\n});\n\n// v2.0.0 config\ncleLogger.init({\n\tlogSinks: [{\n\t\tname: \"custom sink\",\n\t\tlog: logs => {/* your custom logging */},\n\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t// set to true to get feature parity with older version,\n\t\t// please consider your use case before setting\n\t\tpiiSafe: true\n\t},\n\t{\n\t\tname: \"console logger\",\n\t\tlog: consoleLogger,\n\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t// set to true to get feature parity with older version,\n\t\t// please consider your use case before setting\n\t\tpiiSafe: true\n\t},\n\t{\n\t\tname: \"remote logger\",\n\t\tlog: remoteLogger(\"http://log.output/api\"),\n\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t// set to true to get feature parity with older version,\n\t\t// please consider your use case before setting\n\t\tpiiSafe: true\t\t\n\t}]\n});\n```\n\n### v1.0.0 [2019-09-12]\n\n- Initial release\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.0.4-beta.20200203.3","_nodeVersion":"12.14.1","_npmVersion":"6.13.6","dist":{"integrity":"sha512-wZW/Ukc4fmsZY69YmfzVqvv4xwnDKvyQKnKk7drf+bS8wg4as/lZ6pEAvi6Go4b532Kom3wC2fWYzlnxUBzfuw==","shasum":"740ba02c2fa21bf5a4b3a23297e4124181675664","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.0.4-beta.20200203.3.tgz","fileCount":17,"unpackedSize":66233,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeOKbpCRA9TVsSAnZWagAADxEP/1OfafIYwHhHO/PD1e4U\ncEmOFxI4Ap0mM1LuDfr+0VH5WXuOqYICq6pj4pU7Be0Iu2+e5vwqIQgItMwB\n/C9ihtiyMZ79Gey9ra6RlFVV1UX6g0PboDjeNTV75M0hxPN+HBuEtu0JQil6\nx2OpCzXcQyRAulmHuX0bZrjXKEqx7LBxG3xKw5LdP7Fb6lEOQuPaZtYuy8hp\n34D2IaQvZvxJP6+9ZAVEAFvEwr6fGhNIWm0R/EWHKKAfU2YXp6dvgv9QiVWJ\nU4AOvSR1+JFKHj9GH/wJ8ChcYt0sFMxt3d6XBvKwd5/Ndz20psdTA/sH2Syz\nyj+R8hC/tAC8IrX98UGQG+e+pkJlM6yWAMTY67J+E5EeBEXEWl1NtCc68xUl\nmpp0heh6wJtncxMlQSNmmSTB0AGSYB1S+ayqWSBe4G/pHsviQiHM4rh9Mnh4\niBjwm4SACOb/7ZiJr67jEdLsDN3adRg7p4/zFdoHXvtJd16kdeTV6CfIiXlu\nA0fDeogAgS6huHFev1pOUbM4fV2XHUUWbDpnj4kKrWVk1RNZDH+QpLGi7zjg\n0TsPeTAO49OSV/aQ+pNg5vs7mfoeWJPNCS77oAlLOrd3CDOSEjqVp2cSzmHm\nl26x9R+YWUs+IwdqW4Zog4X4rGRZx5XSrE8VJ0qly44Ss3bm7PCzXzvmrzWg\nHiSA\r\n=eDFV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIE0VOpFNabQ9lOu+eQX1QEGP8sf8Ov4Y7xzvKKA0OLRzAiEAuPNS0zTwvhj1bcG01ie2NbpbOdlHsMO25JgVhzJ+SvA="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.0.4-beta.20200203.3_1580771048686_0.9269782332688326"},"_hasShrinkwrap":false},"2.0.4":{"name":"@prftesp/cle-logger","version":"2.0.4","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@2.0.4","_nodeVersion":"12.14.1","_npmVersion":"6.13.6","dist":{"integrity":"sha512-ePSWWKRJqUmBw4p7Zilakk97PDTmX3mYeDRlWBBv8Ngza39bl+vY6Pyz1URVPdH2KgbWOJ4t1r0Spx4hSjqoCQ==","shasum":"b72d310d2d42b546b8add8ed8553e00c09d45f1e","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.0.4.tgz","fileCount":17,"unpackedSize":66134,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeOKeZCRA9TVsSAnZWagAAnCUP/1uPSpBpjXYBJ+MCey2e\nP8tYNmk9MhuXHpge1Q3WhKDVAq8NaoYqiK73hFsEf8GiwfyjPMh8acPxZEcA\nqW5L5pVMgpU9iMgazB5mZj2WA066Benz3YNc8jimhjq2vcGYBd8UBl9uqDnD\ng5I1L2MBDRgulqcMvtzVaLQC2oj9POqXPilYuZsRErSWEDmVCPMYfq9r9TvG\n2AINOEPXMSC1EOTUrF/FsRgsHNNuHh3faNJvXWvbs5cHlgNTZEy4Eq+OD20Q\nLrzJAjaVzg767gtJ7dbnFS5ozQ+2fyU0BN+hOuoPibRBGuHv8lv2snb3TGS2\nWSWWX5hLlzsjO/FFzvER9rJatp+eolX05R1d0nS1qH46yO52HTCAmn51En92\nTiYLllOjdHc75/9pelzxdCc70tZb0ZKT30wwjgmvgwPm9y3/xWzXzmN1pSGG\ni4QPVS5RZ4sVBygAw7pDIJswZCQ5ohd0o+rjrlvvqKgyyA51p22ZsZNoQJVl\nnFulY9TmcqNBtOdHZ/JLgcHsItwnGsXNPAqp4qhyDKPOWi7T/ZPh4BX72PdG\n21PYduKzP8gvs8Jcw4h8GtOgC2xt0X8Ifnc/sXFg6LqIi+kEp6KBJxIfc9A3\n/a73QIYVxx6cHsyyUgJqpiJKlePaOfQshKG89/Yp87bwT5j8kzx6fQGgyRe2\n0Dfp\r\n=RPIo\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDhjUfSs2jMBQxOzHP3ZTEmrlFoaDnKMT1+D4mEZHKxNwIhALMgLXdFHbMLn3ynIThdFkwE7pNNS39JWCe1pSaeat9I"}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.0.4_1580771224618_0.4357855516959046"},"_hasShrinkwrap":false},"2.0.5-beta.20200211.2":{"name":"@prftesp/cle-logger","version":"2.0.5-beta.20200211.2","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿# CLE Logger\n\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\n\n## Documentation\n\n- [Target Environment](#target-environment)\n- [Simple Example](#simple-example)\n- [Installation](#installation)\n- [Features](#features)\n- [Configuration](#configuration)\n- [API](#api)\n  - [Log sinks](#log-sinks)\n  - [Logging statements](#logging-statements)\n  - [Logging parameters](#logging-parameters)\n  - [Logging constants](#logging-constants)\n  - [Log codes](#log-codes)\n  - [Registering Twilio Actions](#registering-twilio-actions)\n  - [Twilio Event Hooks](#twilio-event-hooks)\n  - [Version](#version)\n- [Log Object](#log-object)\n- [Handling PII Data](#handling-pii-data)\n- [Advanced Configuration](#advanced-configuration)\n  - [Remote logging](#remote-logging)\n- [Changelog](#changelog)\n\n## Target Environment\n\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\n\n## Simple Example\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"console sink\",\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\tpiiSafe: true,\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n});\n\n// will log message to console\ncleLogger.info(\"log message\");\n```\n\n## Installation\n\n```bash\n$ npm install @prftesp/cle-logger\n```\n\n## Features\n\n- Runs in Node.js and the browser\n- Remote logging\n- Multiple log sinks\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\n- Remote minimum log level overrides\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\n- Support for tagging logs with custom component names or correlation tokens\n- Send Twilio event hook data to a custom backend\n\n## Configuration\n\nTo configure the logger call the `init` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\n\n**Available options**\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// Twilio account sid, required for use with CLE\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\n\n\t// Will be associated with all logs,\n\t// if not provided, a unique uuid will be generated\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\n\n\t// An array of that will receive an array of log objects,\n\t// and can optionally return a promise. Please see the\n\t// \"Log Sinks\" section for more information.\n\tlogSinks: [\n\t\t{\n\t\t\t// name of the log sink\n\t\t\tname: \"exampleConsoleSink\",\n\t\t\t// log handler function\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\t// This flag defines if the log message is processed by this sink when the contains\n\t\t\t// PII parameter of the log statement is true. piiSafe of true indicates all log\n\t\t\t// messages are allowed (even when contains PII is true); piiSafe of false indicates\n\t\t\t// only log messages with contains PII set to false will be processed by this log sink.\n\t\t\t// Defaults to false\n\t\t\tpiiSafe: true,\n\t\t\t// log level for the sink, defaults to INFO\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n\n\t// A way to suppress error messages if set to false, defaults to true\n\tlogErrorsToConsole: true,\n\n\t// If logging Twilio Flex Actions, provide a list of actions\n\t// to be excluded from logs\n\texcludedFlexActionNames: [\"SendTyping\"],\n\n\t// A custom endpoint to fetch log level overrides\n\t// NOTE: the provided url should always end with 'accountSid=',\n\t// the library will append the accountSid\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\n\n\t// An instance of ('@twilio/flex-ui').Manager.\n\t// If provided, it will be used to get the worker sid and automatically include\n\t// it in 'customProperties' log property and with every flex action log\n\ttwilioFlexManager: manager,\n\n\t// To send data from Twilio event hooks, to a custom backend,\n\t// configure one or more endpoints for each event type\n\ttwilioEventHooks: {\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\n\t},\n});\n```\n\n## API\n\nThe logger can be imported as an object in CommonJs or ES6 syntax:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n// or\nconst cleLogger = require(\"@prftesp/cle-logger\");\n```\n\nAlternatively, all top level members of the `cleLogger` are available as named exports and can be imported separately:\n\n```js\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\n\ninit({\n\tlogSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: logLevels.VERBOSE\n\t}],\n});\n\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\n\n// vs\n\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ninit({\n\tcleLogger.logSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: cleLogger.consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}],\n});\n\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\n```\n\n### Log sinks\n\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the `logSinks` config. The logSinks configuration defines an array of objects with each object defining the log sink activity. All provided log sinks will be executed in parallel. A log sink configuration must include the following properties:\n\n- `name`: this is an identifiable name for the logger and must be unique per log sink defined\n- `log`: this indicates the logic to process on each batch of logs processed by the sink. There are several built in processing functions that can be used or you can insert any code inline with this definition. The built in processing functions are `consoleLogger` which will send the log statements to the attached console or `remoteLogger` which will send the log statements to the specified url provided as a definition on the parameter (i.e. `remoteLogger(\"https://mylogger.url\"`). For a custom log processing implementation, the logic must be async operations and must return a Promise object. The custom log method will receive an array of log objects for you to handle. You can find out more about the log object type in the [Log Object](#log-object) section. Here is an example of a custom log function:\n\n```js\nlogs => {\n\t// log each received log to the console separately\n\tlogs.forEach(log => console.log(log));\n\treturn Promise.resolve();\n};\n```\n\n- `logLevel`: defines the minimum default log level that will be processed by this log sink. Log levels lower than the minimum level will not be processed by this log sink. The minimum default log level can be overridden at run time by the value returned from the overall `minLogLevelOverrideUrl` that is set up in the library `init`.\n- `piiSafe`: indicates if log messages with a `containsPII` parameter value of true should be processed by the log sink. If `containsPII` parameter value on the log message is true and piiSafe is false then the log message will not be processed by this log sink, if piiSafe is true then it will. If the `containsPII` parameter value is false on the log message than it is always processed by the log sink regardless of the piiSafe value.\n\nCustom log sink configuration example. In this example, the custom code executed for each log statement is to append the log into a file. Note how a Promise is returned as required:\n\n```js\n{\n\t// name of the log sink\n\tname: \"exampleConsoleSink\",\n\t// log handler function - this is where you put your custom handling code and\n\t// remember to always return a promise. If your operation is not asynchronous,\n\t// you can do this by returning Promise.resolve().\n\tlog: logs => {\n\t\tfs.appendFile(\n\t\t\t\"./log.txt\",\n\t\t\t`${JSON.stringify(logs)}`,\n\t\t\tconsole.log\n\t\t);\n\t\treturn Promise.resolve();\n\t},\n\t// should this log sink accept PII data, defaults to false\n\tpiiSafe: true,\n\t// log level for the sink, defaults to INFO\n\tlogLevel: cleLogger.logLevels.VERBOSE\n}\n```\n\nExample log sink configuration that uses a custom coded sink and both standard console and remote logging sinks:\n\n```js\nconst fs = require(\"fs\");\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"fileSink\",\n\t\t\tlog: logs => {\n\t\t\t\tfs.appendFile(\"./log.txt\", `${JSON.stringify(logs)}`, console.log);\n\t\t\t\treturn Promise.resolve();\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"remoteSink\",\n\t\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\n\t\t\tlogLevel: cleLogger.logLevels.WARN,\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"consoleSink\",\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n});\n```\n\n### Logging statements\n\nTo create logs, you can use one of the logging function available. They all take the same arguments, described below, and log at a corresponding log level. For example, if you use the `cleLogger.verbose` logging function, and your log sink is configured with `logLevel: cleLogger.logLevels.VERBOSE`, that log sink will receive the log. If your log sink is configured with a higher log level, for example `logLevel: cleLogger.logLevels.INFO`, then it would not receive the verbose log. \n\nHere are the available logging functions:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(...);\ncleLogger.info(...);\ncleLogger.warn(...);\ncleLogger.error(...);\ncleLogger.critical(...);\n```\n\nThe functions are all async, return `Promise<void>`, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\n\n### Logging parameters\n\nAll logging functions take the same parameters:\n\n- `message`: the only required property, no logs will be generated without it\n  - type: string\n  - required: yes\n- `additionalData`: any additional log data you would like to include with the log, can be any data type\n  - type: any\n  - required: no\n  - default value: null\n- `customProperties`: a single level deep object of important properties you would like to distinguish from general log data, like various sids CLE uses it to index these properties in Application Insights. For CLE, try to use keys found in `cleLogger.customPropertyKeys`\n  - type: object\n  - required: no\n  - default value: {}\n- `componentName`: another useful way to 'tag' your logs for easier searching. Can be any string, but for CLE, try to use available options in `cleLogger.componentNames`\n  - type: string\n  - required: no\n  - default value: \"\"\n- `logCode`: log code, can be any number used to categorize logs. For defined ranges see the [log codes](#log-codes) section.\n  - type: number\n  - required: no\n  - default value: 0\n- `correlationToken`: correlation token to be associated with this log only\n  - type: string\n  - required: no\n  - default value: token provided in `init`, or a generated token if one wasn't provided\n- `containsPII`: does the log contain PII data, defaults to false. If set to true, the log will be passed in only to those log sinks that have `piiSafe` set to true. For more info see the [handling PII data](#handling-pii-data) section.\n  - type: boolean\n  - required: no\n  - default value: false\n\nExample verbose logging statement:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(\n\t\"Log message\", // message\n\t{ data: \"test\" }, // additionalData\n\t{ [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" }, // customProperties\n\tcleLogger.componentNames.SESSION, // componentName\n\t70123, // logCode\n\t\"my-token\", // correlationToken\n\ttrue, // containsPII\n);\n```\n\n### Logging constants\n\n**Logging levels**\n\nLogging level constants should be used when configuring the log level of a [log sink](#log-sinks).\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.logLevels.VERBOSE; // verbose\ncleLogger.logLevels.INFO; // info\ncleLogger.logLevels.WARN; // warn\ncleLogger.logLevels.ERROR; // error\ncleLogger.logLevels.CRITICAL; // critical\n```\n\n**Component names**\n\nDefines a set of commonly used components that should be used for the `componentName` parameter value of a log statement if you are logging from one of these common components in a Twilio implementation.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.componentNames.SESSION;\ncleLogger.componentNames.DIALER;\ncleLogger.componentNames.DASHBOARD;\ncleLogger.componentNames.AFTERCALL;\ncleLogger.componentNames.SUPERVISOR;\n```\n\n**Custom property keys**\n\nDefines a set of commonly used key that should be used when defining the custom properties object that is supplied in the `customProperties` parameter of any logging statement.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.customPropertyKeys.CHANNEL_SID;\ncleLogger.customPropertyKeys.TASK_SID;\ncleLogger.customPropertyKeys.CALL_SID;\ncleLogger.customPropertyKeys.WORKFLOW_SID;\ncleLogger.customPropertyKeys.WORKER_SID;\n```\n\n### Log codes\n\nIt can be helpful when investigating issues to only view logs related to specific areas of a system. Use Log Codes to give a category definition for what area of the contact center the log activity occurred within. Use numbers within the following pre-defined log code ranges or define custom log codes by using your own numbers higher than 300,000.\n\n- 100 000 - 109 999 => CALL_STATUS\n- 110 000 - 119 999 => IVR\n- 120 000 - 129 999 => ROUTING\n- 130 000 - 139 999 => AGENT_ACTION\n- 140 000 - 149 999 => TRANSFER\n- 150 000 - 159 999 => RM_INTEGRATION\n- 160 000 - 169 999 => WFM_INTEGRATION\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\n\n### Registering Twilio Actions\n\nIt is important to log information from the Twilio Flex UI for all implementations even when there is little customization happening. Many developers find it useful to generate log activity when some, or any, of the Twilio Actions occur within the Twilio Flex UI. Twilio Actions are events that arise from the Twilio Flex UI base implementation and indicate when certain activity is occurring - such as an agent hanging up a call. The CLE Logger library can be setup to automatically register all, or selected, Twilio Actions such that right after any code tied to the Action event has been executed a log statement is written indicating the Action occurred. This log statement will also automatically include the action event payload. To enable this logging capability, use the following approach to register the Twilio Actions. This only needs to be performed once during the initialization of the Twilio Flex UI page on the client.\n\nExample in a Twilio Flex UI plugin:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n/**\n * @param flex { typeof import('@twilio/flex-ui') }\n * @param manager { import('@twilio/flex-ui').Manager }\n */\ninit(flex, manager) {\n\tcleLogger.init({\n\t\taccountSid: \"your-account-sid\"\n\t});\n\n\tcleLogger.registerTwilioActions(flex);\n}\n```\n\n**Note**: A Twilio account sid must be provided during `init` with the `accountSid` parameter, otherwise the actions won't be logged.\n\nThe logger will loop through all actions found under `flex.Actions.actions`, and attach `after` event handlers to log the action name and payload. Any private properties found in the payload (starting with '\\_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Not all Action events need to be logged and by using the `excludedFlexActionNames` property in the logger configuration the Action events not desired can be ignored. Any actions names configured in `excludedFlexActionNames` will not be included in the logs.\n\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\n\n**Twilio Actions log levels**\n\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of `INFO`.\n\n```js\nAcceptTask: INFO;\nCancelTransfer: INFO;\nCompleteTask: INFO;\nHangupCall: INFO;\nHideDirectory: VERBOSE;\nHistoryGo: VERBOSE;\nHistoryGoBack: VERBOSE;\nHistoryGoForward: VERBOSE;\nHistoryPush: VERBOSE;\nHistoryReplace: VERBOSE;\nHoldCall: INFO;\nHoldParticipant: INFO;\nKickParticipant: INFO;\nLogout: INFO;\nMonitorCall: INFO;\nNavigateToView: INFO;\nRejectTask: INFO;\nSelectTask: INFO;\nSelectTaskInSupervisor: INFO;\nSelectWorkerInSupervisor: INFO;\nSendMessage: INFO;\nSendTyping: VERBOSE;\nSetActivity: INFO;\nSetInputText: VERBOSE;\nShowDirectory: VERBOSE;\nStopMonitoringCall: INFO;\nToggleMute: INFO;\nToggleSidebar: VERBOSE;\nTransferTask: INFO;\nUnholdCall: INFO;\nUnholdParticipant: INFO;\nWrapupTask: INFO;\n```\n\n### Twilio Event Hooks\n\nCurrently, the Twilio console only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with `twilioEventHooks` option, and use the following functions to send event data to a custom endpoint.\n\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is `application/x-www-form-urlencoded` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// unrelated init properties omitted from this example.\n\t// include in the twilioEventHooks array an entry for only the event hooks\n\t// the executing code is going to relay. For example, if the code is only\n\t// handling task router web hook events then you would only need to define\n\t// the taskRouter relay url here.\n\ttwilioEventHooks: {\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\n\t},\n});\n\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\n```\n\nExample Azure Function hook for debugger events:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\nmodule.exports = async function(context, req) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\t},\n\t});\n\n\t// log to console\n\tcleLogger.info(\"Received Twilio debugger event\");\n\n\t// send event data to a configured url\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\n};\n```\n\n**NOTE WHEN USING THIS FUNCTIONALITY IN TWILIO FUNCTIONS:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to `x-www-form-urlencoded` format.\n\nExample Twilio function task router relay using the `form-urlencoded` npm package to convert the data to `x-www-form-urlencoded` format:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\nconst formurlencoded = require(\"form-urlencoded\").default;\n\nexports.handler = async function(context, event, callback) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\t},\n\t});\n\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\n\n\tcallback();\n};\n```\n\n### Version\n\nVersion will be automatically included in all logs.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.version; // 1.0.0\n```\n\n## Log Object\n\nThe following is the type definition of the log object received by [log sinks](#log-sinks). It consists of properties provided in log messages, as well as some that are automatically inserted like the `eventDate`. You can use this information to customize your logs inside a custom log sink (e.g. filter out unwanted logs, modify some of the properties to include more data, or any kind of conditional logic based on the value of these properties). If using the built in `remoteLogger` log processor with your custom backend, you can expect an array of these objects to be sent to your log endpoint. For more info about implementing a custom logging endpoint see the [remote logging](#remote-logging) section.\n\n```js\n{\n\t// Account sid if provided during init\n\taccountSid: string;\n\n\t// Any additional log data to be included with the log\n\tadditionalData: unknown;\n\n\t// Component name, if provided\n\tcomponentName: string;\n\n\t// Generated or configured token\n\tcorrelationToken: string;\n\n\t// Current UTC datetime in ISO 8601 format\n\teventDate: string;\n\n\t// Log level\n\tlevel: string;\n\n\t// Log code, if provided\n\tlogCode: number;\n\n\t// Log message\n\tmessage: string;\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\tproperties: object;\n\n\t// BrowserFunction if the environment is the browser\n\t// CloudFunction if the environment is Node.js\n\tsource: string;\n\n\t// Indicates that the log contains PII data\n\tcontainsPII: boolean;\n\n\t// Logging library version\n\tloggerVersion: string;\n}\n```\n\n## Handling PII Data\n\nThe logger provides support for helping to protect log messages or data that need to include PII (Personally Identifiable Information). While the logger has several mechanisms that can be used to flag logs that contain PII and block PII from going to particular log sinks, it is still ultimately the developers responsibility to ensure PII is flagged or marked appropriately for the logger to know when it exists - the logger cannot automatically detect if PII is part of a log statement or data object. The general guideline is that developers should avoid including any PII data in log information whenever possible. In the instances where PII data must be included there are several mechanisms the logger supports to help indicate PII data and protect where it is sent.\n\nA `containsPII` parameter exists on log statement methods. This flag should be set to true if there is PII data, or any potential chance of it, in the log message, additional data or custom properties data. When this flag is set to true then it will only allow log sinks that have the `piiSafe` configuration set to true to receive and process the log message. `piiSafe` indicates on a log sink whether that log sink receiver is able to internally store PII data OR has logic to strip PII data that is tagged in the message or from pre-configured additional data fields before the log is written to any storage or output device. When sending data to any `remoteLogger` endpoint the log sink can be marked `piiSafe` only as long as data is appropriately tagged in messages and/or the names of custom properties that might have PII values in additional data are pre-configured AND the endpoint implementer has verified and ensured their receiving code will remove marked PII data immediately before sending logs to any output or storage media.\n\nTo tag any part of the message parameter in a log statement as containing PII within the actual log message use the `tagPII` utility. The `tagPII` utility will indicate to a remote backend that the text between the tags should be stripped or masked before sending logs to any output or storage media.\n\nExample tagged output:\n\n```js\nimport { tagPII } from \"@prftesp/cle-logger\";\n\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\n```\n\n## Advanced Configuration\n\n### Remote logging\n\nThe library provides a built in way to transmit logs to a remote http endpoint using the `remoteLogger` logs processor. The `remoteLogger` can be directed at a known listener such as CLE or you can create your own custom log listener. In addition, the log level in effect can be controlled remotely setting the `minLogLevelOverrideUrl` to an http address that will return the current log level to use instead of any defined default logLevel on each `logSink`. The `minLogLevelOverrideUrl` can point to a known central API such as CLE or you can create your own endpoint.\n\nThe following sections outline how to create your own endpoints for either receiving log messages from the `remoteLogger` log sink or providing the current log level override to it.\n\n**Remote log endpoint**\n\nTo implement your own remote logging endpoint, all you have to do is accept `POST` requests, with a JSON payload of an array of [log objects](#log-object), and return either `{ success: true }` or `{ success: false }`. The endpoint should always return a `200 OK` status even if your server code fails so that the CLE logger does not cause failures in the business logic in which it is embedded. A good use-case would be leveraging a serverless api platform to output logs to a queue.\n\nExample of an Azure Function that can receive requests from the `remoteLogger` log sink:\n\n```js\nmodule.exports = async function(context, req) {\n\tcontext.log(req.body); // body will have the log array\n\n\tcontext.res = {\n\t\tstatus: 200,\n\t\tbody: { success: true },\n\t};\n};\n```\n\n**Remote log level override**\n\nThe `minLogLevelOverrideUrl` configuration property is used to provide an endpoint that accepts `GET` requests and a `accountSid` query string parameter. `accountSid` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the `minLogLevel` configuration property. If null is returned, the `minLogLevel` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a `200 OK` status code and use the `success` property to indicate failure.\n\nExample success response:\n\n```json\n{\n\t\"success\": true,\n\t\"minLogLevel\": \"warn\"\n}\n```\n\nExample error response:\n\n```json\n{\n\t\"success\": false,\n\t\"minLogLevel\": null\n}\n```\n\n## Changelog\n\n### v2.0.5\n\n**New features**\n\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the worker sid and automatically include it in `customProperties` log property.\n\n**Other updates**\n\n- Updated documentation based on user feedback.\n\n### v2.0.4\n\n**Bug fixes**\n\n- Fixed issue with logging to console in Twilio Functions. The `consoleLogger` will now output the log object as serialized json.\n\n### v2.0.0 [2019-12-20]\n\n**New features**\n\n- All logging output is now configured using the `logSinks` property. The library provides two commonly used loggers - `consoleLogger` and a `remoteLogger` that accepts the log url as a parameter. [Configuration](#configuration)\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\n- Log functions have a new optional `containsPII` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\n- A new helper function `tagPII` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\n\n**Breaking changes**\n\n- `customLogSink` configuration renamed to `logSinks`\n- Removed `disableRemoteLogging` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\n- Removed `logToConsole` configuration. This is now configured as a log sink with a `consoleLogger` helper function.\n- Removed `logUrl` configuration. This is now configured as a log sink with a `remoteLogger` helper function.\n- Removed `minLogLevel` configuration. This is now configured per log sink.\n\n**Migration guide**\n\n```js\nimport cleLogger from \"@prftesp/cle-logger\";\n\n// v1.0.0 and older config\ncleLogger.init({\n\tdisableRemoteLogSinks: true, // no direct replacement\n\tlogToConsole: true, // replaced by the \"console logger\" sink\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\n\tcustomLogSinks: [\n\t\t{\n\t\t\tname: \"custom sink\",\n\t\t\tlog: logs => {\n\t\t\t\t/* your custom logging */\n\t\t\t},\n\t\t},\n\t],\n});\n\n// v2.0.0 config\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"custom sink\",\n\t\t\tlog: logs => {\n\t\t\t\t/* your custom logging */\n\t\t\t},\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"console logger\",\n\t\t\tlog: consoleLogger,\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"remote logger\",\n\t\t\tlog: remoteLogger(\"http://log.output/api\"),\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t],\n});\n```\n\n### v1.0.0 [2019-09-12]\n\n- Initial release\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.0.5-beta.20200211.2","_nodeVersion":"12.14.1","_npmVersion":"6.13.7","dist":{"integrity":"sha512-Tcpr4+jWrqs6R0aerjHQAW4dI8n8YmvErLZn7w9QNaXYjModf/a+cKVwesHEcdZI9lq3hzRq9HFynxrM0bF2Qw==","shasum":"94b66dde4cfd5040a5251234a736f7acf34c4376","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.0.5-beta.20200211.2.tgz","fileCount":17,"unpackedSize":75236,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeQuYHCRA9TVsSAnZWagAA1YEP/A2Skbbkvnzs+bAdYI69\nUm+LERs9o93Zpk6JDlGVVD3slA8h9g3AApq948iOCL1pHPjK26aL/c25NPv+\nMnP9wgQElbxShcY2VrTMCr16DT22tw+exIsyfsc4eyctWtmfdcPQNmT4DP6y\nZnhuSglgvF7NPEgyX57+035p+nwygriIOq6le+SNuCgJm9/UK1n6gBuFuupF\nkS20DFkWWGEoBaKG9RFd8XJrloQi+zZJK6Zcb+gg311R2ylEiAe7C0J/sg8p\n3OCAMvz+jC8VUZ9U/Ij7K8BBZh0MIOOjlbM/wLyzUnR/z3ij9/gtRbzLJDmW\nfj+3vsRiDuWejTvaJjnimMlxt4jklObYb8gqEVxTmZqH3mW1StHm8JZmPnKN\nzRSHxDo19EcU46rj5JjCf39m1UcN9QJUbwf+TtJtZvSQORZFr77QLWsokv1Y\nz+LI3hRc16GlAg23aTWr3aUXa2QXRyk4cOPCBBZ2fWDrTcduZekhbvR/JBHF\nCxEpiVUOQh7CHfpNoJqLX/vUxDYkbKv4JEvO5+PA6/JdpsompUcQPyUBVPB8\nFTmSy8Io97lVfgEkEaVIgCmUYEKVBEp46CSwuJCEkt2W13CJTgD85jsmTw7f\nK/dv5D4W+qErk4jVPN22PRwxZEvM0xgXAapFgTrDX0f7VwGGCqbA07K1bhg3\nzNQB\r\n=x7od\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHtCuTcq9jMjogNPlu1m2UaeTAo7LzoV+B4MXH4DSrH5AiEA0eHgHKQsP6806g+OknZhPgTS+kVuBufkZ1xBxPXk2pU="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.0.5-beta.20200211.2_1581442567241_0.1655159055659161"},"_hasShrinkwrap":false},"2.0.5":{"name":"@prftesp/cle-logger","version":"2.0.5","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@2.0.5","_nodeVersion":"12.14.1","_npmVersion":"6.13.7","dist":{"integrity":"sha512-d43zmuSh2rLlteOjxcd0uPXY80d/U3X3c01avrU0w13qWn0GkrnIQGDZQldFpUnT1u1kqLL/pcHKZMYRdmwrjg==","shasum":"a714f78826e5fff2e0547ce796ccf03fa4e039ea","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.0.5.tgz","fileCount":17,"unpackedSize":75137,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeQvGICRA9TVsSAnZWagAAm9kP/3trp9JuYyRukjcHMV8y\n7FKZP6FaY+Q2jzFPolOoS5dN3NRH8p5OratcBZRDIO35Rv7Jjt7kDiagoWWP\ntQKNyXVYWeLd0OAoGpuorzwzlCavePZF+mODXlIeuR3KbwWFrrKrVCbNJGtP\n2Cvb1UNHS0z5AVTL1EXOCQFF85h6uZp3aKlQ93TYoaSM1E5dlUEYha+Gzz8V\ndRuhqA4LlgkzeBKKz6K+2Lj5m2cA6nC6JiV/s0hoqNO5EAng4V0PnDqT/PHQ\nNzU9MBDDbiG3WJe1cWb/ZmcIYUoFvxHf8H43ly8WxXIW+tukBnFWvuC6x8Yf\n9tRLmKN/kaRkfWA9RneVvB6qxH4oUHxnsMSgO1DRj7hNB1DhWGodjN2fpsW4\ncSMMkh5CeOKCHnmwJ7vw+6+Z+t/gGSNwQTr8jZd7NHhjiew5dAdJhLCD3lEF\n6A3U61DRWJyDl32+5TF2KJy34OyJJrm0PJ2OBQ1r7bmEygCvNTPSuKzMXka4\nFVrg3X7Kig8ZLDxicIZKQtBKQq3HeDEBTc8Jqtr82FFc2D6s8qLgO9JjLvmL\n9cMdpMU6IG+Yp/EKS03JgFmcMhLrWmL0qsC9QHlPeAXgqf6+n8wSq/vRUt/e\nvoVoU/W5Ao/d+oGgUab6HvNKu+lkl4XPZynLpapZhTubh/4Zdoo8NYiBm03f\noAb6\r\n=IK/G\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCdvlh1B4kJtdYHv+p9YHjvJMk5CIu8BhFhq8r8q70iEgIgAwfB/TEOsPzfD1T7nA2vCG4G3FI9G6xHO+oORzMYnuI="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.0.5_1581445511580_0.1470266260033024"},"_hasShrinkwrap":false},"2.0.6-beta.0":{"name":"@prftesp/cle-logger","version":"2.0.6-beta.0","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿# CLE Logger\r\n\r\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\r\n\r\n## Documentation\r\n\r\n- [Target Environment](#target-environment)\r\n- [Simple Example](#simple-example)\r\n- [Installation](#installation)\r\n- [Features](#features)\r\n- [Configuration](#configuration)\r\n- [API](#api)\r\n  - [Log sinks](#log-sinks)\r\n  - [Logging statements](#logging-statements)\r\n  - [Logging parameters](#logging-parameters)\r\n  - [Logging constants](#logging-constants)\r\n  - [Log codes](#log-codes)\r\n  - [Registering Twilio Actions](#registering-twilio-actions)\r\n  - [Twilio Event Hooks](#twilio-event-hooks)\r\n  - [Version](#version)\r\n- [Log Object](#log-object)\r\n- [Handling PII Data](#handling-pii-data)\r\n- [Advanced Configuration](#advanced-configuration)\r\n  - [Remote logging](#remote-logging)\r\n- [Changelog](#changelog)\r\n\r\n## Target Environment\r\n\r\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\r\n\r\n## Simple Example\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\tname: \"console sink\",\r\n\t\t\tlog: cleLogger.consoleLogger,\r\n\t\t\tpiiSafe: true,\r\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\r\n\t\t},\r\n\t],\r\n});\r\n\r\n// will log message to console\r\ncleLogger.info(\"log message\");\r\n```\r\n\r\n## Installation\r\n\r\n```bash\r\n$ npm install @prftesp/cle-logger\r\n```\r\n\r\n## Features\r\n\r\n- Runs in Node.js and the browser\r\n- Remote logging\r\n- Multiple log sinks\r\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\r\n- Remote minimum log level overrides\r\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\r\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\r\n- Support for tagging logs with custom component names or correlation tokens\r\n- Send Twilio event hook data to a custom backend\r\n\r\n## Configuration\r\n\r\nTo configure the logger call the `init` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\r\n\r\n**Available options**\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// Twilio account sid, required for use with CLE\r\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\r\n\r\n\t// Will be associated with all logs,\r\n\t// if not provided, a unique uuid will be generated\r\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\r\n\t// An array of that will receive an array of log objects,\r\n\t// and can optionally return a promise. Please see the\r\n\t// \"Log Sinks\" section for more information.\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\t// name of the log sink\r\n\t\t\tname: \"exampleConsoleSink\",\r\n\t\t\t// log handler function\r\n\t\t\tlog: cleLogger.consoleLogger,\r\n\t\t\t// This flag defines if the log message is processed by this sink when the contains\r\n\t\t\t// PII parameter of the log statement is true. piiSafe of true indicates all log\r\n\t\t\t// messages are allowed (even when contains PII is true); piiSafe of false indicates\r\n\t\t\t// only log messages with contains PII set to false will be processed by this log sink.\r\n\t\t\t// Defaults to false\r\n\t\t\tpiiSafe: true,\r\n\t\t\t// log level for the sink, defaults to INFO\r\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\r\n\t\t},\r\n\t],\r\n\r\n\t// A way to suppress error messages if set to false, defaults to true\r\n\tlogErrorsToConsole: true,\r\n\r\n\t// If logging Twilio Flex Actions, provide a list of actions\r\n\t// to be excluded from logs\r\n\texcludedFlexActionNames: [\"SendTyping\"],\r\n\r\n\t// A custom endpoint to fetch log level overrides\r\n\t// NOTE: the provided url should always end with 'accountSid=',\r\n\t// the library will append the accountSid\r\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\r\n\r\n\t// An instance of ('@twilio/flex-ui').Manager.\r\n\t// If provided, it will be used to get the worker sid and call sid (during a call), and \r\n\t// automatically include it in 'customProperties' log property and with every flex action log\r\n\ttwilioFlexManager: manager,\r\n\r\n\t// To send data from Twilio event hooks, to a custom backend,\r\n\t// configure one or more endpoints for each event type\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\r\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\r\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\r\n\t},\r\n});\r\n```\r\n\r\n## API\r\n\r\nThe logger can be imported as an object in CommonJs or ES6 syntax:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n// or\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n```\r\n\r\nAlternatively, all top level members of the `cleLogger` are available as named exports and can be imported separately:\r\n\r\n```js\r\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tlogSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\r\n\r\n// vs\r\n\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tcleLogger.logSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\r\n```\r\n\r\n### Log sinks\r\n\r\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the `logSinks` config. The logSinks configuration defines an array of objects with each object defining the log sink activity. All provided log sinks will be executed in parallel. A log sink configuration must include the following properties:\r\n\r\n- `name`: this is an identifiable name for the logger and must be unique per log sink defined\r\n- `log`: this indicates the logic to process on each batch of logs processed by the sink. There are several built in processing functions that can be used or you can insert any code inline with this definition. The built in processing functions are `consoleLogger` which will send the log statements to the attached console or `remoteLogger` which will send the log statements to the specified url provided as a definition on the parameter (i.e. `remoteLogger(\"https://mylogger.url\"`). For a custom log processing implementation, the logic must be async operations and must return a Promise object. The custom log method will receive an array of log objects for you to handle. You can find out more about the log object type in the [Log Object](#log-object) section. Here is an example of a custom log function:\r\n\r\n```js\r\nlogs => {\r\n\t// log each received log to the console separately\r\n\tlogs.forEach(log => console.log(log));\r\n\treturn Promise.resolve();\r\n};\r\n```\r\n\r\n- `logLevel`: defines the minimum default log level that will be processed by this log sink. Log levels lower than the minimum level will not be processed by this log sink. The minimum default log level can be overridden at run time by the value returned from the overall `minLogLevelOverrideUrl` that is set up in the library `init`.\r\n- `piiSafe`: indicates if log messages with a `containsPII` parameter value of true should be processed by the log sink. If `containsPII` parameter value on the log message is true and piiSafe is false then the log message will not be processed by this log sink, if piiSafe is true then it will. If the `containsPII` parameter value is false on the log message than it is always processed by the log sink regardless of the piiSafe value.\r\n\r\nCustom log sink configuration example. In this example, the custom code executed for each log statement is to append the log into a file. Note how a Promise is returned as required:\r\n\r\n```js\r\n{\r\n\t// name of the log sink\r\n\tname: \"exampleConsoleSink\",\r\n\t// log handler function - this is where you put your custom handling code and\r\n\t// remember to always return a promise. If your operation is not asynchronous,\r\n\t// you can do this by returning Promise.resolve().\r\n\tlog: logs => {\r\n\t\tfs.appendFile(\r\n\t\t\t\"./log.txt\",\r\n\t\t\t`${JSON.stringify(logs)}`,\r\n\t\t\tconsole.log\r\n\t\t);\r\n\t\treturn Promise.resolve();\r\n\t},\r\n\t// should this log sink accept PII data, defaults to false\r\n\tpiiSafe: true,\r\n\t// log level for the sink, defaults to INFO\r\n\tlogLevel: cleLogger.logLevels.VERBOSE\r\n}\r\n```\r\n\r\nExample log sink configuration that uses a custom coded sink and both standard console and remote logging sinks:\r\n\r\n```js\r\nconst fs = require(\"fs\");\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\ncleLogger.init({\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\tname: \"fileSink\",\r\n\t\t\tlog: logs => {\r\n\t\t\t\tfs.appendFile(\"./log.txt\", `${JSON.stringify(logs)}`, console.log);\r\n\t\t\t\treturn Promise.resolve();\r\n\t\t\t},\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"remoteSink\",\r\n\t\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\r\n\t\t\tlogLevel: cleLogger.logLevels.WARN,\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"consoleSink\",\r\n\t\t\tlog: cleLogger.consoleLogger,\r\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\r\n\t\t},\r\n\t],\r\n});\r\n```\r\n\r\n### Logging statements\r\n\r\nTo create logs, you can use one of the logging function available. They all take the same arguments, described below, and log at a corresponding log level. For example, if you use the `cleLogger.verbose` logging function, and your log sink is configured with `logLevel: cleLogger.logLevels.VERBOSE`, that log sink will receive the log. If your log sink is configured with a higher log level, for example `logLevel: cleLogger.logLevels.INFO`, then it would not receive the verbose log. \r\n\r\nHere are the available logging functions:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(...);\r\ncleLogger.info(...);\r\ncleLogger.warn(...);\r\ncleLogger.error(...);\r\ncleLogger.critical(...);\r\n```\r\n\r\nThe functions are all async, return `Promise<void>`, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\r\n\r\n### Logging parameters\r\n\r\nAll logging functions take the same parameters:\r\n\r\n- `message`: the only required property, no logs will be generated without it\r\n  - type: string\r\n  - required: yes\r\n- `additionalData`: any additional log data you would like to include with the log, can be any data type\r\n  - type: any\r\n  - required: no\r\n  - default value: null\r\n- `customProperties`: a single level deep object of important properties you would like to distinguish from general log data, like various sids CLE uses it to index these properties in Application Insights. For CLE, try to use keys found in `cleLogger.customPropertyKeys`\r\n  - type: object\r\n  - required: no\r\n  - default value: {}\r\n- `componentName`: another useful way to 'tag' your logs for easier searching. Can be any string, but for CLE, try to use available options in `cleLogger.componentNames`\r\n  - type: string\r\n  - required: no\r\n  - default value: \"\"\r\n- `logCode`: log code, can be any number used to categorize logs. For defined ranges see the [log codes](#log-codes) section.\r\n  - type: number\r\n  - required: no\r\n  - default value: 0\r\n- `correlationToken`: correlation token to be associated with this log only\r\n  - type: string\r\n  - required: no\r\n  - default value: token provided in `init`, or a generated token if one wasn't provided\r\n- `containsPII`: does the log contain PII data, defaults to false. If set to true, the log will be passed in only to those log sinks that have `piiSafe` set to true. For more info see the [handling PII data](#handling-pii-data) section.\r\n  - type: boolean\r\n  - required: no\r\n  - default value: false\r\n\r\nExample verbose logging statement:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(\r\n\t\"Log message\", // message\r\n\t{ data: \"test\" }, // additionalData\r\n\t{ [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" }, // customProperties\r\n\tcleLogger.componentNames.SESSION, // componentName\r\n\t70123, // logCode\r\n\t\"my-token\", // correlationToken\r\n\ttrue, // containsPII\r\n);\r\n```\r\n\r\n### Logging constants\r\n\r\n**Logging levels**\r\n\r\nLogging level constants should be used when configuring the log level of a [log sink](#log-sinks).\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.logLevels.VERBOSE; // verbose\r\ncleLogger.logLevels.INFO; // info\r\ncleLogger.logLevels.WARN; // warn\r\ncleLogger.logLevels.ERROR; // error\r\ncleLogger.logLevels.CRITICAL; // critical\r\n```\r\n\r\n**Component names**\r\n\r\nDefines a set of commonly used components that should be used for the `componentName` parameter value of a log statement if you are logging from one of these common components in a Twilio implementation.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.componentNames.SESSION;\r\ncleLogger.componentNames.DIALER;\r\ncleLogger.componentNames.DASHBOARD;\r\ncleLogger.componentNames.AFTERCALL;\r\ncleLogger.componentNames.SUPERVISOR;\r\n```\r\n\r\n**Custom property keys**\r\n\r\nDefines a set of commonly used key that should be used when defining the custom properties object that is supplied in the `customProperties` parameter of any logging statement.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.customPropertyKeys.CHANNEL_SID;\r\ncleLogger.customPropertyKeys.TASK_SID;\r\ncleLogger.customPropertyKeys.CALL_SID;\r\ncleLogger.customPropertyKeys.WORKFLOW_SID;\r\ncleLogger.customPropertyKeys.WORKER_SID;\r\n```\r\n\r\n### Log codes\r\n\r\nIt can be helpful when investigating issues to only view logs related to specific areas of a system. Use Log Codes to give a category definition for what area of the contact center the log activity occurred within. Use numbers within the following pre-defined log code ranges or define custom log codes by using your own numbers higher than 300,000.\r\n\r\n- 100 000 - 109 999 => CALL_STATUS\r\n- 110 000 - 119 999 => IVR\r\n- 120 000 - 129 999 => ROUTING\r\n- 130 000 - 139 999 => AGENT_ACTION\r\n- 140 000 - 149 999 => TRANSFER\r\n- 150 000 - 159 999 => RM_INTEGRATION\r\n- 160 000 - 169 999 => WFM_INTEGRATION\r\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\r\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\r\n\r\n### Registering Twilio Actions\r\n\r\nIt is important to log information from the Twilio Flex UI for all implementations even when there is little customization happening. Many developers find it useful to generate log activity when some, or any, of the Twilio Actions occur within the Twilio Flex UI. Twilio Actions are events that arise from the Twilio Flex UI base implementation and indicate when certain activity is occurring - such as an agent hanging up a call. The CLE Logger library can be setup to automatically register all, or selected, Twilio Actions such that right after any code tied to the Action event has been executed a log statement is written indicating the Action occurred. This log statement will also automatically include the action event payload. To enable this logging capability, use the following approach to register the Twilio Actions. This only needs to be performed once during the initialization of the Twilio Flex UI page on the client.\r\n\r\nExample in a Twilio Flex UI plugin:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\n/**\r\n * @param flex { typeof import('@twilio/flex-ui') }\r\n * @param manager { import('@twilio/flex-ui').Manager }\r\n */\r\ninit(flex, manager) {\r\n\tcleLogger.init({\r\n\t\taccountSid: \"your-account-sid\"\r\n\t});\r\n\r\n\tcleLogger.registerTwilioActions(flex);\r\n}\r\n```\r\n\r\n**Note**: A Twilio account sid must be provided during `init` with the `accountSid` parameter, otherwise the actions won't be logged.\r\n\r\nThe logger will loop through all actions found under `flex.Actions.actions`, and attach `after` event handlers to log the action name and payload. Any private properties found in the payload (starting with '\\_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Not all Action events need to be logged and by using the `excludedFlexActionNames` property in the logger configuration the Action events not desired can be ignored. Any actions names configured in `excludedFlexActionNames` will not be included in the logs.\r\n\r\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\r\n\r\n**Twilio Actions log levels**\r\n\r\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of `INFO`.\r\n\r\n```js\r\nAcceptTask: INFO;\r\nCancelTransfer: INFO;\r\nCompleteTask: INFO;\r\nHangupCall: INFO;\r\nHideDirectory: VERBOSE;\r\nHistoryGo: VERBOSE;\r\nHistoryGoBack: VERBOSE;\r\nHistoryGoForward: VERBOSE;\r\nHistoryPush: VERBOSE;\r\nHistoryReplace: VERBOSE;\r\nHoldCall: INFO;\r\nHoldParticipant: INFO;\r\nKickParticipant: INFO;\r\nLogout: INFO;\r\nMonitorCall: INFO;\r\nNavigateToView: INFO;\r\nRejectTask: INFO;\r\nSelectTask: INFO;\r\nSelectTaskInSupervisor: INFO;\r\nSelectWorkerInSupervisor: INFO;\r\nSendMessage: INFO;\r\nSendTyping: VERBOSE;\r\nSetActivity: INFO;\r\nSetInputText: VERBOSE;\r\nShowDirectory: VERBOSE;\r\nStopMonitoringCall: INFO;\r\nToggleMute: INFO;\r\nToggleSidebar: VERBOSE;\r\nTransferTask: INFO;\r\nUnholdCall: INFO;\r\nUnholdParticipant: INFO;\r\nWrapupTask: INFO;\r\n```\r\n\r\n### Twilio Event Hooks\r\n\r\nCurrently, the Twilio console only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with `twilioEventHooks` option, and use the following functions to send event data to a custom endpoint.\r\n\r\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is `application/x-www-form-urlencoded` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// unrelated init properties omitted from this example.\r\n\t// include in the twilioEventHooks array an entry for only the event hooks\r\n\t// the executing code is going to relay. For example, if the code is only\r\n\t// handling task router web hook events then you would only need to define\r\n\t// the taskRouter relay url here.\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\r\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\r\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\r\n\t},\r\n});\r\n\r\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\r\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\r\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\r\n```\r\n\r\nExample Azure Function hook for debugger events:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\nmodule.exports = async function(context, req) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\r\n\t\t},\r\n\t});\r\n\r\n\t// log to console\r\n\tcleLogger.info(\"Received Twilio debugger event\");\r\n\r\n\t// send event data to a configured url\r\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\r\n};\r\n```\r\n\r\n**NOTE WHEN USING THIS FUNCTIONALITY IN TWILIO FUNCTIONS:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to `x-www-form-urlencoded` format.\r\n\r\nExample Twilio function task router relay using the `form-urlencoded` npm package to convert the data to `x-www-form-urlencoded` format:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\nconst formurlencoded = require(\"form-urlencoded\").default;\r\n\r\nexports.handler = async function(context, event, callback) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\r\n\t\t},\r\n\t});\r\n\r\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\r\n\r\n\tcallback();\r\n};\r\n```\r\n\r\n### Version\r\n\r\nVersion will be automatically included in all logs.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.version; // 1.0.0\r\n```\r\n\r\n## Log Object\r\n\r\nThe following is the type definition of the log object received by [log sinks](#log-sinks). It consists of properties provided in log messages, as well as some that are automatically inserted like the `eventDate`. You can use this information to customize your logs inside a custom log sink (e.g. filter out unwanted logs, modify some of the properties to include more data, or any kind of conditional logic based on the value of these properties). If using the built in `remoteLogger` log processor with your custom backend, you can expect an array of these objects to be sent to your log endpoint. For more info about implementing a custom logging endpoint see the [remote logging](#remote-logging) section.\r\n\r\n```js\r\n{\r\n\t// Account sid if provided during init\r\n\taccountSid: string;\r\n\r\n\t// Any additional log data to be included with the log\r\n\tadditionalData: unknown;\r\n\r\n\t// Component name, if provided\r\n\tcomponentName: string;\r\n\r\n\t// Generated or configured token\r\n\tcorrelationToken: string;\r\n\r\n\t// Current UTC datetime in ISO 8601 format\r\n\teventDate: string;\r\n\r\n\t// Log level\r\n\tlevel: string;\r\n\r\n\t// Log code, if provided\r\n\tlogCode: number;\r\n\r\n\t// Log message\r\n\tmessage: string;\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\tproperties: object;\r\n\r\n\t// BrowserFunction if the environment is the browser\r\n\t// CloudFunction if the environment is Node.js\r\n\tsource: string;\r\n\r\n\t// Indicates that the log contains PII data\r\n\tcontainsPII: boolean;\r\n\r\n\t// Logging library version\r\n\tloggerVersion: string;\r\n}\r\n```\r\n\r\n## Handling PII Data\r\n\r\nThe logger provides support for helping to protect log messages or data that need to include PII (Personally Identifiable Information). While the logger has several mechanisms that can be used to flag logs that contain PII and block PII from going to particular log sinks, it is still ultimately the developers responsibility to ensure PII is flagged or marked appropriately for the logger to know when it exists - the logger cannot automatically detect if PII is part of a log statement or data object. The general guideline is that developers should avoid including any PII data in log information whenever possible. In the instances where PII data must be included there are several mechanisms the logger supports to help indicate PII data and protect where it is sent.\r\n\r\nA `containsPII` parameter exists on log statement methods. This flag should be set to true if there is PII data, or any potential chance of it, in the log message, additional data or custom properties data. When this flag is set to true then it will only allow log sinks that have the `piiSafe` configuration set to true to receive and process the log message. `piiSafe` indicates on a log sink whether that log sink receiver is able to internally store PII data OR has logic to strip PII data that is tagged in the message or from pre-configured additional data fields before the log is written to any storage or output device. When sending data to any `remoteLogger` endpoint the log sink can be marked `piiSafe` only as long as data is appropriately tagged in messages and/or the names of custom properties that might have PII values in additional data are pre-configured AND the endpoint implementer has verified and ensured their receiving code will remove marked PII data immediately before sending logs to any output or storage media.\r\n\r\nTo tag any part of the message parameter in a log statement as containing PII within the actual log message use the `tagPII` utility. The `tagPII` utility will indicate to a remote backend that the text between the tags should be stripped or masked before sending logs to any output or storage media.\r\n\r\nExample tagged output:\r\n\r\n```js\r\nimport { tagPII } from \"@prftesp/cle-logger\";\r\n\r\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\r\n```\r\n\r\n## Advanced Configuration\r\n\r\n### Remote logging\r\n\r\nThe library provides a built in way to transmit logs to a remote http endpoint using the `remoteLogger` logs processor. The `remoteLogger` can be directed at a known listener such as CLE or you can create your own custom log listener. In addition, the log level in effect can be controlled remotely setting the `minLogLevelOverrideUrl` to an http address that will return the current log level to use instead of any defined default logLevel on each `logSink`. The `minLogLevelOverrideUrl` can point to a known central API such as CLE or you can create your own endpoint.\r\n\r\nThe following sections outline how to create your own endpoints for either receiving log messages from the `remoteLogger` log sink or providing the current log level override to it.\r\n\r\n**Remote log endpoint**\r\n\r\nTo implement your own remote logging endpoint, all you have to do is accept `POST` requests, with a JSON payload of an array of [log objects](#log-object), and return either `{ success: true }` or `{ success: false }`. The endpoint should always return a `200 OK` status even if your server code fails so that the CLE logger does not cause failures in the business logic in which it is embedded. A good use-case would be leveraging a serverless api platform to output logs to a queue.\r\n\r\nExample of an Azure Function that can receive requests from the `remoteLogger` log sink:\r\n\r\n```js\r\nmodule.exports = async function(context, req) {\r\n\tcontext.log(req.body); // body will have the log array\r\n\r\n\tcontext.res = {\r\n\t\tstatus: 200,\r\n\t\tbody: { success: true },\r\n\t};\r\n};\r\n```\r\n\r\n**Remote log level override**\r\n\r\nThe `minLogLevelOverrideUrl` configuration property is used to provide an endpoint that accepts `GET` requests and a `accountSid` query string parameter. `accountSid` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the `minLogLevel` configuration property. If null is returned, the `minLogLevel` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a `200 OK` status code and use the `success` property to indicate failure.\r\n\r\nExample success response:\r\n\r\n```json\r\n{\r\n\t\"success\": true,\r\n\t\"minLogLevel\": \"warn\"\r\n}\r\n```\r\n\r\nExample error response:\r\n\r\n```json\r\n{\r\n\t\"success\": false,\r\n\t\"minLogLevel\": null\r\n}\r\n```\r\n\r\n## Changelog\r\n\r\n### v2.0.6\r\n\r\n**New features**\r\n\r\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the call sid during a call, and automatically include it in `customProperties` log property for Flex Actions that happen during that call.\r\n\r\n### v2.0.5\r\n\r\n**New features**\r\n\r\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the worker sid and automatically include it in `customProperties` log property.\r\n\r\n**Other updates**\r\n\r\n- Updated documentation based on user feedback.\r\n\r\n### v2.0.4\r\n\r\n**Bug fixes**\r\n\r\n- Fixed issue with logging to console in Twilio Functions. The `consoleLogger` will now output the log object as serialized json.\r\n\r\n### v2.0.0 [2019-12-20]\r\n\r\n**New features**\r\n\r\n- All logging output is now configured using the `logSinks` property. The library provides two commonly used loggers - `consoleLogger` and a `remoteLogger` that accepts the log url as a parameter. [Configuration](#configuration)\r\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\r\n- Log functions have a new optional `containsPII` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\r\n- A new helper function `tagPII` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\r\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\r\n\r\n**Breaking changes**\r\n\r\n- `customLogSink` configuration renamed to `logSinks`\r\n- Removed `disableRemoteLogging` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\r\n- Removed `logToConsole` configuration. This is now configured as a log sink with a `consoleLogger` helper function.\r\n- Removed `logUrl` configuration. This is now configured as a log sink with a `remoteLogger` helper function.\r\n- Removed `minLogLevel` configuration. This is now configured per log sink.\r\n\r\n**Migration guide**\r\n\r\n```js\r\nimport cleLogger from \"@prftesp/cle-logger\";\r\n\r\n// v1.0.0 and older config\r\ncleLogger.init({\r\n\tdisableRemoteLogSinks: true, // no direct replacement\r\n\tlogToConsole: true, // replaced by the \"console logger\" sink\r\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\r\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\r\n\tcustomLogSinks: [\r\n\t\t{\r\n\t\t\tname: \"custom sink\",\r\n\t\t\tlog: logs => {\r\n\t\t\t\t/* your custom logging */\r\n\t\t\t},\r\n\t\t},\r\n\t],\r\n});\r\n\r\n// v2.0.0 config\r\ncleLogger.init({\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\tname: \"custom sink\",\r\n\t\t\tlog: logs => {\r\n\t\t\t\t/* your custom logging */\r\n\t\t\t},\r\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t\t// set to true to get feature parity with older version,\r\n\t\t\t// please consider your use case before setting\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"console logger\",\r\n\t\t\tlog: consoleLogger,\r\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t\t// set to true to get feature parity with older version,\r\n\t\t\t// please consider your use case before setting\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"remote logger\",\r\n\t\t\tlog: remoteLogger(\"http://log.output/api\"),\r\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t\t// set to true to get feature parity with older version,\r\n\t\t\t// please consider your use case before setting\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t],\r\n});\r\n```\r\n\r\n### v1.0.0 [2019-09-12]\r\n\r\n- Initial release\r\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.0.6-beta.0","_nodeVersion":"10.15.2","_npmVersion":"6.11.3","dist":{"integrity":"sha512-lGZL9H7SJRCFp3viaqIO3xoR6LF3QBeKu6p36yahxEWf9XYNUZ1Ds3n0GYpPrBw4sdfz6uG6e9iKVuQrZzNjUg==","shasum":"4cc37954793a60dfd98ee46eb87214515a13b0df","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.0.6-beta.0.tgz","fileCount":17,"unpackedSize":78041,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeT9FTCRA9TVsSAnZWagAAfgoP/3Zh2dNrzqRx53cCOmKQ\niEAUAfeMoJqHH50ni0G9gi++dVGsuNrSGVws5Ry7AL/GsOhcK1fcqUKnJmAs\nORsz/zAVZgmNko8lqgrRlqWHinXhyyrSBzb+wmhwq/ntc4AaSdyroU6fRl9A\nDendlEyFPswrXfD0sq0TaQ+Xk1MvUhw/IBAU7NFWRtlfbVCZs5f1zz+mQeRh\nSKNO2jRAHYUaI3MRgEkQrh6psqg7jyjYT+1tDP6vSXF0AvULAZslErQv8IA8\nsWXPBMXg1p5mt4x/cM2Frjv7do1srE2P9ROgeveSX3zh6dolyADeBLoKjiPW\nngvBq5NMSyzqKLDgqV+f70NNkI5S+C0NKrcF3OL+Y89kBOWEheE/fh9Nd+GL\nqOu54GxlkandiW+qxp8XYZLbweses9hA5lMSlZQoVXMJxuy16B9NAugMVT3/\nCnrymXvcJsV1CVv24sSEJK8SMRArVwGL2jY6xki7pkEeErwJMGWaAhQOkMpR\nuH4ZMZeoq3PNaHC5/LJJoEoFCuzC/8JXO6t21BR1+pPzFS6wWaXth+JHqTpN\nv7q8mxL/uCZPEL+czPYOT+nKUjETG7ND9LJHhCUVak4hlTbp9XK8OT1CuoGH\n4DKKMjSVZlMct4KGUPQ7oSBCKNsaTgDL7f1SY9kPdeB7amVvmR8Uf5z//jd3\nDNln\r\n=Ps0r\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD92dDcbCMihrG27Mz9qAk/DWn42IJeBrFC4uzZT1oZVgIhAMT6SYSdTu+L61nBOVDXPcVi6Qwmgvh9xLhyzcET9GVG"}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.0.6-beta.0_1582289235248_0.34952030329211103"},"_hasShrinkwrap":false},"2.0.6-beta.1":{"name":"@prftesp/cle-logger","version":"2.0.6-beta.1","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿# CLE Logger\r\n\r\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\r\n\r\n## Documentation\r\n\r\n- [Target Environment](#target-environment)\r\n- [Simple Example](#simple-example)\r\n- [Installation](#installation)\r\n- [Features](#features)\r\n- [Configuration](#configuration)\r\n- [API](#api)\r\n  - [Log sinks](#log-sinks)\r\n  - [Logging statements](#logging-statements)\r\n  - [Logging parameters](#logging-parameters)\r\n  - [Logging constants](#logging-constants)\r\n  - [Log codes](#log-codes)\r\n  - [Registering Twilio Actions](#registering-twilio-actions)\r\n  - [Twilio Event Hooks](#twilio-event-hooks)\r\n  - [Version](#version)\r\n- [Log Object](#log-object)\r\n- [Handling PII Data](#handling-pii-data)\r\n- [Advanced Configuration](#advanced-configuration)\r\n  - [Remote logging](#remote-logging)\r\n- [Changelog](#changelog)\r\n\r\n## Target Environment\r\n\r\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\r\n\r\n## Simple Example\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\tname: \"console sink\",\r\n\t\t\tlog: cleLogger.consoleLogger,\r\n\t\t\tpiiSafe: true,\r\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\r\n\t\t},\r\n\t],\r\n});\r\n\r\n// will log message to console\r\ncleLogger.info(\"log message\");\r\n```\r\n\r\n## Installation\r\n\r\n```bash\r\n$ npm install @prftesp/cle-logger\r\n```\r\n\r\n## Features\r\n\r\n- Runs in Node.js and the browser\r\n- Remote logging\r\n- Multiple log sinks\r\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\r\n- Remote minimum log level overrides\r\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\r\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\r\n- Support for tagging logs with custom component names or correlation tokens\r\n- Send Twilio event hook data to a custom backend\r\n\r\n## Configuration\r\n\r\nTo configure the logger call the `init` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\r\n\r\n**Available options**\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// Twilio account sid, required for use with CLE\r\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\r\n\r\n\t// Will be associated with all logs,\r\n\t// if not provided, a unique uuid will be generated\r\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\r\n\t// An array of that will receive an array of log objects,\r\n\t// and can optionally return a promise. Please see the\r\n\t// \"Log Sinks\" section for more information.\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\t// name of the log sink\r\n\t\t\tname: \"exampleConsoleSink\",\r\n\t\t\t// log handler function\r\n\t\t\tlog: cleLogger.consoleLogger,\r\n\t\t\t// This flag defines if the log message is processed by this sink when the contains\r\n\t\t\t// PII parameter of the log statement is true. piiSafe of true indicates all log\r\n\t\t\t// messages are allowed (even when contains PII is true); piiSafe of false indicates\r\n\t\t\t// only log messages with contains PII set to false will be processed by this log sink.\r\n\t\t\t// Defaults to false\r\n\t\t\tpiiSafe: true,\r\n\t\t\t// log level for the sink, defaults to INFO\r\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\r\n\t\t},\r\n\t],\r\n\r\n\t// A way to suppress error messages if set to false, defaults to true\r\n\tlogErrorsToConsole: true,\r\n\r\n\t// If logging Twilio Flex Actions, provide a list of actions\r\n\t// to be excluded from logs\r\n\texcludedFlexActionNames: [\"SendTyping\"],\r\n\r\n\t// A custom endpoint to fetch log level overrides\r\n\t// NOTE: the provided url should always end with 'accountSid=',\r\n\t// the library will append the accountSid\r\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\r\n\r\n\t// An instance of ('@twilio/flex-ui').Manager.\r\n\t// If provided, it will be used to get the worker sid and call sid (during a call), and \r\n\t// automatically include it in 'customProperties' log property and with every flex action log\r\n\ttwilioFlexManager: manager,\r\n\r\n\t// To send data from Twilio event hooks, to a custom backend,\r\n\t// configure one or more endpoints for each event type\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\r\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\r\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\r\n\t},\r\n});\r\n```\r\n\r\n## API\r\n\r\nThe logger can be imported as an object in CommonJs or ES6 syntax:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n// or\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n```\r\n\r\nAlternatively, all top level members of the `cleLogger` are available as named exports and can be imported separately:\r\n\r\n```js\r\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tlogSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\r\n\r\n// vs\r\n\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tcleLogger.logSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\r\n```\r\n\r\n### Log sinks\r\n\r\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the `logSinks` config. The logSinks configuration defines an array of objects with each object defining the log sink activity. All provided log sinks will be executed in parallel. A log sink configuration must include the following properties:\r\n\r\n- `name`: this is an identifiable name for the logger and must be unique per log sink defined\r\n- `log`: this indicates the logic to process on each batch of logs processed by the sink. There are several built in processing functions that can be used or you can insert any code inline with this definition. The built in processing functions are `consoleLogger` which will send the log statements to the attached console or `remoteLogger` which will send the log statements to the specified url provided as a definition on the parameter (i.e. `remoteLogger(\"https://mylogger.url\"`). For a custom log processing implementation, the logic must be async operations and must return a Promise object. The custom log method will receive an array of log objects for you to handle. You can find out more about the log object type in the [Log Object](#log-object) section. Here is an example of a custom log function:\r\n\r\n```js\r\nlogs => {\r\n\t// log each received log to the console separately\r\n\tlogs.forEach(log => console.log(log));\r\n\treturn Promise.resolve();\r\n};\r\n```\r\n\r\n- `logLevel`: defines the minimum default log level that will be processed by this log sink. Log levels lower than the minimum level will not be processed by this log sink. The minimum default log level can be overridden at run time by the value returned from the overall `minLogLevelOverrideUrl` that is set up in the library `init`.\r\n- `piiSafe`: indicates if log messages with a `containsPII` parameter value of true should be processed by the log sink. If `containsPII` parameter value on the log message is true and piiSafe is false then the log message will not be processed by this log sink, if piiSafe is true then it will. If the `containsPII` parameter value is false on the log message than it is always processed by the log sink regardless of the piiSafe value.\r\n\r\nCustom log sink configuration example. In this example, the custom code executed for each log statement is to append the log into a file. Note how a Promise is returned as required:\r\n\r\n```js\r\n{\r\n\t// name of the log sink\r\n\tname: \"exampleConsoleSink\",\r\n\t// log handler function - this is where you put your custom handling code and\r\n\t// remember to always return a promise. If your operation is not asynchronous,\r\n\t// you can do this by returning Promise.resolve().\r\n\tlog: logs => {\r\n\t\tfs.appendFile(\r\n\t\t\t\"./log.txt\",\r\n\t\t\t`${JSON.stringify(logs)}`,\r\n\t\t\tconsole.log\r\n\t\t);\r\n\t\treturn Promise.resolve();\r\n\t},\r\n\t// should this log sink accept PII data, defaults to false\r\n\tpiiSafe: true,\r\n\t// log level for the sink, defaults to INFO\r\n\tlogLevel: cleLogger.logLevels.VERBOSE\r\n}\r\n```\r\n\r\nExample log sink configuration that uses a custom coded sink and both standard console and remote logging sinks:\r\n\r\n```js\r\nconst fs = require(\"fs\");\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\ncleLogger.init({\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\tname: \"fileSink\",\r\n\t\t\tlog: logs => {\r\n\t\t\t\tfs.appendFile(\"./log.txt\", `${JSON.stringify(logs)}`, console.log);\r\n\t\t\t\treturn Promise.resolve();\r\n\t\t\t},\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"remoteSink\",\r\n\t\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\r\n\t\t\tlogLevel: cleLogger.logLevels.WARN,\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"consoleSink\",\r\n\t\t\tlog: cleLogger.consoleLogger,\r\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\r\n\t\t},\r\n\t],\r\n});\r\n```\r\n\r\n### Logging statements\r\n\r\nTo create logs, you can use one of the logging function available. They all take the same arguments, described below, and log at a corresponding log level. For example, if you use the `cleLogger.verbose` logging function, and your log sink is configured with `logLevel: cleLogger.logLevels.VERBOSE`, that log sink will receive the log. If your log sink is configured with a higher log level, for example `logLevel: cleLogger.logLevels.INFO`, then it would not receive the verbose log. \r\n\r\nHere are the available logging functions:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(...);\r\ncleLogger.info(...);\r\ncleLogger.warn(...);\r\ncleLogger.error(...);\r\ncleLogger.critical(...);\r\n```\r\n\r\nThe functions are all async, return `Promise<void>`, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\r\n\r\n### Logging parameters\r\n\r\nAll logging functions take the same parameters:\r\n\r\n- `message`: the only required property, no logs will be generated without it\r\n  - type: string\r\n  - required: yes\r\n- `additionalData`: any additional log data you would like to include with the log, can be any data type\r\n  - type: any\r\n  - required: no\r\n  - default value: null\r\n- `customProperties`: a single level deep object of important properties you would like to distinguish from general log data, like various sids CLE uses it to index these properties in Application Insights. For CLE, try to use keys found in `cleLogger.customPropertyKeys`\r\n  - type: object\r\n  - required: no\r\n  - default value: {}\r\n- `componentName`: another useful way to 'tag' your logs for easier searching. Can be any string, but for CLE, try to use available options in `cleLogger.componentNames`\r\n  - type: string\r\n  - required: no\r\n  - default value: \"\"\r\n- `logCode`: log code, can be any number used to categorize logs. For defined ranges see the [log codes](#log-codes) section.\r\n  - type: number\r\n  - required: no\r\n  - default value: 0\r\n- `correlationToken`: correlation token to be associated with this log only\r\n  - type: string\r\n  - required: no\r\n  - default value: token provided in `init`, or a generated token if one wasn't provided\r\n- `containsPII`: does the log contain PII data, defaults to false. If set to true, the log will be passed in only to those log sinks that have `piiSafe` set to true. For more info see the [handling PII data](#handling-pii-data) section.\r\n  - type: boolean\r\n  - required: no\r\n  - default value: false\r\n\r\nExample verbose logging statement:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(\r\n\t\"Log message\", // message\r\n\t{ data: \"test\" }, // additionalData\r\n\t{ [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" }, // customProperties\r\n\tcleLogger.componentNames.SESSION, // componentName\r\n\t70123, // logCode\r\n\t\"my-token\", // correlationToken\r\n\ttrue, // containsPII\r\n);\r\n```\r\n\r\n### Logging constants\r\n\r\n**Logging levels**\r\n\r\nLogging level constants should be used when configuring the log level of a [log sink](#log-sinks).\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.logLevels.VERBOSE; // verbose\r\ncleLogger.logLevels.INFO; // info\r\ncleLogger.logLevels.WARN; // warn\r\ncleLogger.logLevels.ERROR; // error\r\ncleLogger.logLevels.CRITICAL; // critical\r\n```\r\n\r\n**Component names**\r\n\r\nDefines a set of commonly used components that should be used for the `componentName` parameter value of a log statement if you are logging from one of these common components in a Twilio implementation.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.componentNames.SESSION;\r\ncleLogger.componentNames.DIALER;\r\ncleLogger.componentNames.DASHBOARD;\r\ncleLogger.componentNames.AFTERCALL;\r\ncleLogger.componentNames.SUPERVISOR;\r\n```\r\n\r\n**Custom property keys**\r\n\r\nDefines a set of commonly used key that should be used when defining the custom properties object that is supplied in the `customProperties` parameter of any logging statement.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.customPropertyKeys.CHANNEL_SID;\r\ncleLogger.customPropertyKeys.TASK_SID;\r\ncleLogger.customPropertyKeys.CALL_SID;\r\ncleLogger.customPropertyKeys.WORKFLOW_SID;\r\ncleLogger.customPropertyKeys.WORKER_SID;\r\n```\r\n\r\n### Log codes\r\n\r\nIt can be helpful when investigating issues to only view logs related to specific areas of a system. Use Log Codes to give a category definition for what area of the contact center the log activity occurred within. Use numbers within the following pre-defined log code ranges or define custom log codes by using your own numbers higher than 300,000.\r\n\r\n- 100 000 - 109 999 => CALL_STATUS\r\n- 110 000 - 119 999 => IVR\r\n- 120 000 - 129 999 => ROUTING\r\n- 130 000 - 139 999 => AGENT_ACTION\r\n- 140 000 - 149 999 => TRANSFER\r\n- 150 000 - 159 999 => RM_INTEGRATION\r\n- 160 000 - 169 999 => WFM_INTEGRATION\r\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\r\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\r\n\r\n### Registering Twilio Actions\r\n\r\nIt is important to log information from the Twilio Flex UI for all implementations even when there is little customization happening. Many developers find it useful to generate log activity when some, or any, of the Twilio Actions occur within the Twilio Flex UI. Twilio Actions are events that arise from the Twilio Flex UI base implementation and indicate when certain activity is occurring - such as an agent hanging up a call. The CLE Logger library can be setup to automatically register all, or selected, Twilio Actions such that right after any code tied to the Action event has been executed a log statement is written indicating the Action occurred. This log statement will also automatically include the action event payload. To enable this logging capability, use the following approach to register the Twilio Actions. This only needs to be performed once during the initialization of the Twilio Flex UI page on the client.\r\n\r\nExample in a Twilio Flex UI plugin:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\n/**\r\n * @param flex { typeof import('@twilio/flex-ui') }\r\n * @param manager { import('@twilio/flex-ui').Manager }\r\n */\r\ninit(flex, manager) {\r\n\tcleLogger.init({\r\n\t\taccountSid: \"your-account-sid\"\r\n\t});\r\n\r\n\tcleLogger.registerTwilioActions(flex);\r\n}\r\n```\r\n\r\n**Note**: A Twilio account sid must be provided during `init` with the `accountSid` parameter, otherwise the actions won't be logged.\r\n\r\nThe logger will loop through all actions found under `flex.Actions.actions`, and attach `after` event handlers to log the action name and payload. Any private properties found in the payload (starting with '\\_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Not all Action events need to be logged and by using the `excludedFlexActionNames` property in the logger configuration the Action events not desired can be ignored. Any actions names configured in `excludedFlexActionNames` will not be included in the logs.\r\n\r\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\r\n\r\n**Twilio Actions log levels**\r\n\r\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of `INFO`.\r\n\r\n```js\r\nAcceptTask: INFO;\r\nCancelTransfer: INFO;\r\nCompleteTask: INFO;\r\nHangupCall: INFO;\r\nHideDirectory: VERBOSE;\r\nHistoryGo: VERBOSE;\r\nHistoryGoBack: VERBOSE;\r\nHistoryGoForward: VERBOSE;\r\nHistoryPush: VERBOSE;\r\nHistoryReplace: VERBOSE;\r\nHoldCall: INFO;\r\nHoldParticipant: INFO;\r\nKickParticipant: INFO;\r\nLogout: INFO;\r\nMonitorCall: INFO;\r\nNavigateToView: INFO;\r\nRejectTask: INFO;\r\nSelectTask: INFO;\r\nSelectTaskInSupervisor: INFO;\r\nSelectWorkerInSupervisor: INFO;\r\nSendMessage: INFO;\r\nSendTyping: VERBOSE;\r\nSetActivity: INFO;\r\nSetInputText: VERBOSE;\r\nShowDirectory: VERBOSE;\r\nStopMonitoringCall: INFO;\r\nToggleMute: INFO;\r\nToggleSidebar: VERBOSE;\r\nTransferTask: INFO;\r\nUnholdCall: INFO;\r\nUnholdParticipant: INFO;\r\nWrapupTask: INFO;\r\n```\r\n\r\n### Twilio Event Hooks\r\n\r\nCurrently, the Twilio console only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with `twilioEventHooks` option, and use the following functions to send event data to a custom endpoint.\r\n\r\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is `application/x-www-form-urlencoded` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// unrelated init properties omitted from this example.\r\n\t// include in the twilioEventHooks array an entry for only the event hooks\r\n\t// the executing code is going to relay. For example, if the code is only\r\n\t// handling task router web hook events then you would only need to define\r\n\t// the taskRouter relay url here.\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\r\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\r\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\r\n\t},\r\n});\r\n\r\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\r\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\r\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\r\n```\r\n\r\nExample Azure Function hook for debugger events:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\nmodule.exports = async function(context, req) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\r\n\t\t},\r\n\t});\r\n\r\n\t// log to console\r\n\tcleLogger.info(\"Received Twilio debugger event\");\r\n\r\n\t// send event data to a configured url\r\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\r\n};\r\n```\r\n\r\n**NOTE WHEN USING THIS FUNCTIONALITY IN TWILIO FUNCTIONS:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to `x-www-form-urlencoded` format.\r\n\r\nExample Twilio function task router relay using the `form-urlencoded` npm package to convert the data to `x-www-form-urlencoded` format:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\nconst formurlencoded = require(\"form-urlencoded\").default;\r\n\r\nexports.handler = async function(context, event, callback) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\r\n\t\t},\r\n\t});\r\n\r\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\r\n\r\n\tcallback();\r\n};\r\n```\r\n\r\n### Version\r\n\r\nVersion will be automatically included in all logs.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.version; // 1.0.0\r\n```\r\n\r\n## Log Object\r\n\r\nThe following is the type definition of the log object received by [log sinks](#log-sinks). It consists of properties provided in log messages, as well as some that are automatically inserted like the `eventDate`. You can use this information to customize your logs inside a custom log sink (e.g. filter out unwanted logs, modify some of the properties to include more data, or any kind of conditional logic based on the value of these properties). If using the built in `remoteLogger` log processor with your custom backend, you can expect an array of these objects to be sent to your log endpoint. For more info about implementing a custom logging endpoint see the [remote logging](#remote-logging) section.\r\n\r\n```js\r\n{\r\n\t// Account sid if provided during init\r\n\taccountSid: string;\r\n\r\n\t// Any additional log data to be included with the log\r\n\tadditionalData: unknown;\r\n\r\n\t// Component name, if provided\r\n\tcomponentName: string;\r\n\r\n\t// Generated or configured token\r\n\tcorrelationToken: string;\r\n\r\n\t// Current UTC datetime in ISO 8601 format\r\n\teventDate: string;\r\n\r\n\t// Log level\r\n\tlevel: string;\r\n\r\n\t// Log code, if provided\r\n\tlogCode: number;\r\n\r\n\t// Log message\r\n\tmessage: string;\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\tproperties: object;\r\n\r\n\t// BrowserFunction if the environment is the browser\r\n\t// CloudFunction if the environment is Node.js\r\n\tsource: string;\r\n\r\n\t// Indicates that the log contains PII data\r\n\tcontainsPII: boolean;\r\n\r\n\t// Logging library version\r\n\tloggerVersion: string;\r\n}\r\n```\r\n\r\n## Handling PII Data\r\n\r\nThe logger provides support for helping to protect log messages or data that need to include PII (Personally Identifiable Information). While the logger has several mechanisms that can be used to flag logs that contain PII and block PII from going to particular log sinks, it is still ultimately the developers responsibility to ensure PII is flagged or marked appropriately for the logger to know when it exists - the logger cannot automatically detect if PII is part of a log statement or data object. The general guideline is that developers should avoid including any PII data in log information whenever possible. In the instances where PII data must be included there are several mechanisms the logger supports to help indicate PII data and protect where it is sent.\r\n\r\nA `containsPII` parameter exists on log statement methods. This flag should be set to true if there is PII data, or any potential chance of it, in the log message, additional data or custom properties data. When this flag is set to true then it will only allow log sinks that have the `piiSafe` configuration set to true to receive and process the log message. `piiSafe` indicates on a log sink whether that log sink receiver is able to internally store PII data OR has logic to strip PII data that is tagged in the message or from pre-configured additional data fields before the log is written to any storage or output device. When sending data to any `remoteLogger` endpoint the log sink can be marked `piiSafe` only as long as data is appropriately tagged in messages and/or the names of custom properties that might have PII values in additional data are pre-configured AND the endpoint implementer has verified and ensured their receiving code will remove marked PII data immediately before sending logs to any output or storage media.\r\n\r\nTo tag any part of the message parameter in a log statement as containing PII within the actual log message use the `tagPII` utility. The `tagPII` utility will indicate to a remote backend that the text between the tags should be stripped or masked before sending logs to any output or storage media.\r\n\r\nExample tagged output:\r\n\r\n```js\r\nimport { tagPII } from \"@prftesp/cle-logger\";\r\n\r\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\r\n```\r\n\r\n## Advanced Configuration\r\n\r\n### Remote logging\r\n\r\nThe library provides a built in way to transmit logs to a remote http endpoint using the `remoteLogger` logs processor. The `remoteLogger` can be directed at a known listener such as CLE or you can create your own custom log listener. In addition, the log level in effect can be controlled remotely setting the `minLogLevelOverrideUrl` to an http address that will return the current log level to use instead of any defined default logLevel on each `logSink`. The `minLogLevelOverrideUrl` can point to a known central API such as CLE or you can create your own endpoint.\r\n\r\nThe following sections outline how to create your own endpoints for either receiving log messages from the `remoteLogger` log sink or providing the current log level override to it.\r\n\r\n**Remote log endpoint**\r\n\r\nTo implement your own remote logging endpoint, all you have to do is accept `POST` requests, with a JSON payload of an array of [log objects](#log-object), and return either `{ success: true }` or `{ success: false }`. The endpoint should always return a `200 OK` status even if your server code fails so that the CLE logger does not cause failures in the business logic in which it is embedded. A good use-case would be leveraging a serverless api platform to output logs to a queue.\r\n\r\nExample of an Azure Function that can receive requests from the `remoteLogger` log sink:\r\n\r\n```js\r\nmodule.exports = async function(context, req) {\r\n\tcontext.log(req.body); // body will have the log array\r\n\r\n\tcontext.res = {\r\n\t\tstatus: 200,\r\n\t\tbody: { success: true },\r\n\t};\r\n};\r\n```\r\n\r\n**Remote log level override**\r\n\r\nThe `minLogLevelOverrideUrl` configuration property is used to provide an endpoint that accepts `GET` requests and a `accountSid` query string parameter. `accountSid` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the `minLogLevel` configuration property. If null is returned, the `minLogLevel` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a `200 OK` status code and use the `success` property to indicate failure.\r\n\r\nExample success response:\r\n\r\n```json\r\n{\r\n\t\"success\": true,\r\n\t\"minLogLevel\": \"warn\"\r\n}\r\n```\r\n\r\nExample error response:\r\n\r\n```json\r\n{\r\n\t\"success\": false,\r\n\t\"minLogLevel\": null\r\n}\r\n```\r\n\r\n## Changelog\r\n\r\n### v2.0.6\r\n\r\n**New features**\r\n\r\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the call sid during a call, and automatically include it in `customProperties` log property for Flex Actions that happen during that call.\r\n\r\n### v2.0.5\r\n\r\n**New features**\r\n\r\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the worker sid and automatically include it in `customProperties` log property.\r\n\r\n**Other updates**\r\n\r\n- Updated documentation based on user feedback.\r\n\r\n### v2.0.4\r\n\r\n**Bug fixes**\r\n\r\n- Fixed issue with logging to console in Twilio Functions. The `consoleLogger` will now output the log object as serialized json.\r\n\r\n### v2.0.0 [2019-12-20]\r\n\r\n**New features**\r\n\r\n- All logging output is now configured using the `logSinks` property. The library provides two commonly used loggers - `consoleLogger` and a `remoteLogger` that accepts the log url as a parameter. [Configuration](#configuration)\r\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\r\n- Log functions have a new optional `containsPII` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\r\n- A new helper function `tagPII` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\r\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\r\n\r\n**Breaking changes**\r\n\r\n- `customLogSink` configuration renamed to `logSinks`\r\n- Removed `disableRemoteLogging` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\r\n- Removed `logToConsole` configuration. This is now configured as a log sink with a `consoleLogger` helper function.\r\n- Removed `logUrl` configuration. This is now configured as a log sink with a `remoteLogger` helper function.\r\n- Removed `minLogLevel` configuration. This is now configured per log sink.\r\n\r\n**Migration guide**\r\n\r\n```js\r\nimport cleLogger from \"@prftesp/cle-logger\";\r\n\r\n// v1.0.0 and older config\r\ncleLogger.init({\r\n\tdisableRemoteLogSinks: true, // no direct replacement\r\n\tlogToConsole: true, // replaced by the \"console logger\" sink\r\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\r\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\r\n\tcustomLogSinks: [\r\n\t\t{\r\n\t\t\tname: \"custom sink\",\r\n\t\t\tlog: logs => {\r\n\t\t\t\t/* your custom logging */\r\n\t\t\t},\r\n\t\t},\r\n\t],\r\n});\r\n\r\n// v2.0.0 config\r\ncleLogger.init({\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\tname: \"custom sink\",\r\n\t\t\tlog: logs => {\r\n\t\t\t\t/* your custom logging */\r\n\t\t\t},\r\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t\t// set to true to get feature parity with older version,\r\n\t\t\t// please consider your use case before setting\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"console logger\",\r\n\t\t\tlog: consoleLogger,\r\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t\t// set to true to get feature parity with older version,\r\n\t\t\t// please consider your use case before setting\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"remote logger\",\r\n\t\t\tlog: remoteLogger(\"http://log.output/api\"),\r\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t\t// set to true to get feature parity with older version,\r\n\t\t\t// please consider your use case before setting\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t],\r\n});\r\n```\r\n\r\n### v1.0.0 [2019-09-12]\r\n\r\n- Initial release\r\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.0.6-beta.1","_nodeVersion":"10.15.2","_npmVersion":"6.11.3","dist":{"integrity":"sha512-biEr7xsCHucJOkI0Cg2YZm8eytkOMvKY0Emv2V/4ivONdQHVbTQ8SlE3TkkfrBb6AWjAXlG04ahzi6ndyJ0ScQ==","shasum":"e65dd70bac66ab788132ad89ec8ead27e0dd7250","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.0.6-beta.1.tgz","fileCount":17,"unpackedSize":78230,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeT9M/CRA9TVsSAnZWagAAY7QP/3NI+EO6n1uxvYfQcNg9\ng5RrwC+3905LAlYPk8ZAVahCcXrUZJi5waOaDtiGh2DpKo9xJtsdPIAb24j1\nD8ltyubTr9Km+kharaVn1YnToD5CWu6sLz/NG68WUG6NCSLQ0HPkrdpMziFr\nGt02XFTWgcYV48m/5inXSVQzq8WDa/N1Ld/fDvuqYwA4GA7lSrigmpdpiPXx\n544GiUB98TOgTvHO1OxrbUaVf2FqJT6DoSzG70pKJfTknAQhVf2Jy8bWVRtL\nZVUZXb8Phs8JmEMJcHi110kNQWsC6xqq4fQoCyT2PwxTC32DDL5GAbzIwGF+\nmjWGAfth3r63Hd/zcS5HaD5FHlUVAZrobc7Pn2kLWsIwsMZuFiwXmpLDdOmM\n9QET4NgtJ3WHkXCfghazA1m74Hz8sKDldRk1qLMVoScV33lzzL+DQxajxJMS\npPs9ruImXRBHp//4pNDsr7DuW6PuGqy+V2698abhD1caUxq5g7xaz3e7WntY\nt/IW+6Apxuc5KaGTjit/cQ22r2zCcSB+9EUybAmPz/hRnEDs0YywqT2nlSKj\niAWoZjGJc9EMUOSzxR0KSZHynsd+sUQOcRHAUuoQgdfZNlSAtLMFSFKod3HQ\nW8CG1IFVkEaQH9tTvPkuzfoqJR6AQloEjUcIMPEfSUZGPwtx84DHf4ysCPGg\nyP7a\r\n=xp+m\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICJlMPDVDX7IPLA7KaU3coUZ22WtYir3/SL64EWTmKBUAiAOcdOR6l8xa1VXZb5uQJkcqlX/jUGTg9sNim3BiYM5DQ=="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.0.6-beta.1_1582289727545_0.39829948119696534"},"_hasShrinkwrap":false},"2.0.6-beta.2":{"name":"@prftesp/cle-logger","version":"2.0.6-beta.2","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿# CLE Logger\r\n\r\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\r\n\r\n## Documentation\r\n\r\n- [Target Environment](#target-environment)\r\n- [Simple Example](#simple-example)\r\n- [Installation](#installation)\r\n- [Features](#features)\r\n- [Configuration](#configuration)\r\n- [API](#api)\r\n  - [Log sinks](#log-sinks)\r\n  - [Logging statements](#logging-statements)\r\n  - [Logging parameters](#logging-parameters)\r\n  - [Logging constants](#logging-constants)\r\n  - [Log codes](#log-codes)\r\n  - [Registering Twilio Actions](#registering-twilio-actions)\r\n  - [Twilio Event Hooks](#twilio-event-hooks)\r\n  - [Version](#version)\r\n- [Log Object](#log-object)\r\n- [Handling PII Data](#handling-pii-data)\r\n- [Advanced Configuration](#advanced-configuration)\r\n  - [Remote logging](#remote-logging)\r\n- [Changelog](#changelog)\r\n\r\n## Target Environment\r\n\r\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\r\n\r\n## Simple Example\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\tname: \"console sink\",\r\n\t\t\tlog: cleLogger.consoleLogger,\r\n\t\t\tpiiSafe: true,\r\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\r\n\t\t},\r\n\t],\r\n});\r\n\r\n// will log message to console\r\ncleLogger.info(\"log message\");\r\n```\r\n\r\n## Installation\r\n\r\n```bash\r\n$ npm install @prftesp/cle-logger\r\n```\r\n\r\n## Features\r\n\r\n- Runs in Node.js and the browser\r\n- Remote logging\r\n- Multiple log sinks\r\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\r\n- Remote minimum log level overrides\r\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\r\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\r\n- Support for tagging logs with custom component names or correlation tokens\r\n- Send Twilio event hook data to a custom backend\r\n\r\n## Configuration\r\n\r\nTo configure the logger call the `init` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\r\n\r\n**Available options**\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// Twilio account sid, required for use with CLE\r\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\r\n\r\n\t// Will be associated with all logs,\r\n\t// if not provided, a unique uuid will be generated\r\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\r\n\t// An array of that will receive an array of log objects,\r\n\t// and can optionally return a promise. Please see the\r\n\t// \"Log Sinks\" section for more information.\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\t// name of the log sink\r\n\t\t\tname: \"exampleConsoleSink\",\r\n\t\t\t// log handler function\r\n\t\t\tlog: cleLogger.consoleLogger,\r\n\t\t\t// This flag defines if the log message is processed by this sink when the contains\r\n\t\t\t// PII parameter of the log statement is true. piiSafe of true indicates all log\r\n\t\t\t// messages are allowed (even when contains PII is true); piiSafe of false indicates\r\n\t\t\t// only log messages with contains PII set to false will be processed by this log sink.\r\n\t\t\t// Defaults to false\r\n\t\t\tpiiSafe: true,\r\n\t\t\t// log level for the sink, defaults to INFO\r\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\r\n\t\t},\r\n\t],\r\n\r\n\t// A way to suppress error messages if set to false, defaults to true\r\n\tlogErrorsToConsole: true,\r\n\r\n\t// If logging Twilio Flex Actions, provide a list of actions\r\n\t// to be excluded from logs\r\n\texcludedFlexActionNames: [\"SendTyping\"],\r\n\r\n\t// A custom endpoint to fetch log level overrides\r\n\t// NOTE: the provided url should always end with 'accountSid=',\r\n\t// the library will append the accountSid\r\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\r\n\r\n\t// An instance of ('@twilio/flex-ui').Manager.\r\n\t// If provided, it will be used to get the worker sid and call sid (during a call), and \r\n\t// automatically include it in 'customProperties' log property and with every flex action log\r\n\ttwilioFlexManager: manager,\r\n\r\n\t// To send data from Twilio event hooks, to a custom backend,\r\n\t// configure one or more endpoints for each event type\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\r\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\r\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\r\n\t},\r\n});\r\n```\r\n\r\n## API\r\n\r\nThe logger can be imported as an object in CommonJs or ES6 syntax:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n// or\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n```\r\n\r\nAlternatively, all top level members of the `cleLogger` are available as named exports and can be imported separately:\r\n\r\n```js\r\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tlogSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\r\n\r\n// vs\r\n\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tcleLogger.logSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\r\n```\r\n\r\n### Log sinks\r\n\r\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the `logSinks` config. The logSinks configuration defines an array of objects with each object defining the log sink activity. All provided log sinks will be executed in parallel. A log sink configuration must include the following properties:\r\n\r\n- `name`: this is an identifiable name for the logger and must be unique per log sink defined\r\n- `log`: this indicates the logic to process on each batch of logs processed by the sink. There are several built in processing functions that can be used or you can insert any code inline with this definition. The built in processing functions are `consoleLogger` which will send the log statements to the attached console or `remoteLogger` which will send the log statements to the specified url provided as a definition on the parameter (i.e. `remoteLogger(\"https://mylogger.url\"`). For a custom log processing implementation, the logic must be async operations and must return a Promise object. The custom log method will receive an array of log objects for you to handle. You can find out more about the log object type in the [Log Object](#log-object) section. Here is an example of a custom log function:\r\n\r\n```js\r\nlogs => {\r\n\t// log each received log to the console separately\r\n\tlogs.forEach(log => console.log(log));\r\n\treturn Promise.resolve();\r\n};\r\n```\r\n\r\n- `logLevel`: defines the minimum default log level that will be processed by this log sink. Log levels lower than the minimum level will not be processed by this log sink. The minimum default log level can be overridden at run time by the value returned from the overall `minLogLevelOverrideUrl` that is set up in the library `init`.\r\n- `piiSafe`: indicates if log messages with a `containsPII` parameter value of true should be processed by the log sink. If `containsPII` parameter value on the log message is true and piiSafe is false then the log message will not be processed by this log sink, if piiSafe is true then it will. If the `containsPII` parameter value is false on the log message than it is always processed by the log sink regardless of the piiSafe value.\r\n\r\nCustom log sink configuration example. In this example, the custom code executed for each log statement is to append the log into a file. Note how a Promise is returned as required:\r\n\r\n```js\r\n{\r\n\t// name of the log sink\r\n\tname: \"exampleConsoleSink\",\r\n\t// log handler function - this is where you put your custom handling code and\r\n\t// remember to always return a promise. If your operation is not asynchronous,\r\n\t// you can do this by returning Promise.resolve().\r\n\tlog: logs => {\r\n\t\tfs.appendFile(\r\n\t\t\t\"./log.txt\",\r\n\t\t\t`${JSON.stringify(logs)}`,\r\n\t\t\tconsole.log\r\n\t\t);\r\n\t\treturn Promise.resolve();\r\n\t},\r\n\t// should this log sink accept PII data, defaults to false\r\n\tpiiSafe: true,\r\n\t// log level for the sink, defaults to INFO\r\n\tlogLevel: cleLogger.logLevels.VERBOSE\r\n}\r\n```\r\n\r\nExample log sink configuration that uses a custom coded sink and both standard console and remote logging sinks:\r\n\r\n```js\r\nconst fs = require(\"fs\");\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\ncleLogger.init({\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\tname: \"fileSink\",\r\n\t\t\tlog: logs => {\r\n\t\t\t\tfs.appendFile(\"./log.txt\", `${JSON.stringify(logs)}`, console.log);\r\n\t\t\t\treturn Promise.resolve();\r\n\t\t\t},\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"remoteSink\",\r\n\t\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\r\n\t\t\tlogLevel: cleLogger.logLevels.WARN,\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"consoleSink\",\r\n\t\t\tlog: cleLogger.consoleLogger,\r\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\r\n\t\t},\r\n\t],\r\n});\r\n```\r\n\r\n### Logging statements\r\n\r\nTo create logs, you can use one of the logging function available. They all take the same arguments, described below, and log at a corresponding log level. For example, if you use the `cleLogger.verbose` logging function, and your log sink is configured with `logLevel: cleLogger.logLevels.VERBOSE`, that log sink will receive the log. If your log sink is configured with a higher log level, for example `logLevel: cleLogger.logLevels.INFO`, then it would not receive the verbose log. \r\n\r\nHere are the available logging functions:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(...);\r\ncleLogger.info(...);\r\ncleLogger.warn(...);\r\ncleLogger.error(...);\r\ncleLogger.critical(...);\r\n```\r\n\r\nThe functions are all async, return `Promise<void>`, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\r\n\r\n### Logging parameters\r\n\r\nAll logging functions take the same parameters:\r\n\r\n- `message`: the only required property, no logs will be generated without it\r\n  - type: string\r\n  - required: yes\r\n- `additionalData`: any additional log data you would like to include with the log, can be any data type\r\n  - type: any\r\n  - required: no\r\n  - default value: null\r\n- `customProperties`: a single level deep object of important properties you would like to distinguish from general log data, like various sids CLE uses it to index these properties in Application Insights. For CLE, try to use keys found in `cleLogger.customPropertyKeys`\r\n  - type: object\r\n  - required: no\r\n  - default value: {}\r\n- `componentName`: another useful way to 'tag' your logs for easier searching. Can be any string, but for CLE, try to use available options in `cleLogger.componentNames`\r\n  - type: string\r\n  - required: no\r\n  - default value: \"\"\r\n- `logCode`: log code, can be any number used to categorize logs. For defined ranges see the [log codes](#log-codes) section.\r\n  - type: number\r\n  - required: no\r\n  - default value: 0\r\n- `correlationToken`: correlation token to be associated with this log only\r\n  - type: string\r\n  - required: no\r\n  - default value: token provided in `init`, or a generated token if one wasn't provided\r\n- `containsPII`: does the log contain PII data, defaults to false. If set to true, the log will be passed in only to those log sinks that have `piiSafe` set to true. For more info see the [handling PII data](#handling-pii-data) section.\r\n  - type: boolean\r\n  - required: no\r\n  - default value: false\r\n\r\nExample verbose logging statement:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(\r\n\t\"Log message\", // message\r\n\t{ data: \"test\" }, // additionalData\r\n\t{ [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" }, // customProperties\r\n\tcleLogger.componentNames.SESSION, // componentName\r\n\t70123, // logCode\r\n\t\"my-token\", // correlationToken\r\n\ttrue, // containsPII\r\n);\r\n```\r\n\r\n### Logging constants\r\n\r\n**Logging levels**\r\n\r\nLogging level constants should be used when configuring the log level of a [log sink](#log-sinks).\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.logLevels.VERBOSE; // verbose\r\ncleLogger.logLevels.INFO; // info\r\ncleLogger.logLevels.WARN; // warn\r\ncleLogger.logLevels.ERROR; // error\r\ncleLogger.logLevels.CRITICAL; // critical\r\n```\r\n\r\n**Component names**\r\n\r\nDefines a set of commonly used components that should be used for the `componentName` parameter value of a log statement if you are logging from one of these common components in a Twilio implementation.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.componentNames.SESSION;\r\ncleLogger.componentNames.DIALER;\r\ncleLogger.componentNames.DASHBOARD;\r\ncleLogger.componentNames.AFTERCALL;\r\ncleLogger.componentNames.SUPERVISOR;\r\n```\r\n\r\n**Custom property keys**\r\n\r\nDefines a set of commonly used key that should be used when defining the custom properties object that is supplied in the `customProperties` parameter of any logging statement.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.customPropertyKeys.CHANNEL_SID;\r\ncleLogger.customPropertyKeys.TASK_SID;\r\ncleLogger.customPropertyKeys.CALL_SID;\r\ncleLogger.customPropertyKeys.WORKFLOW_SID;\r\ncleLogger.customPropertyKeys.WORKER_SID;\r\n```\r\n\r\n### Log codes\r\n\r\nIt can be helpful when investigating issues to only view logs related to specific areas of a system. Use Log Codes to give a category definition for what area of the contact center the log activity occurred within. Use numbers within the following pre-defined log code ranges or define custom log codes by using your own numbers higher than 300,000.\r\n\r\n- 100 000 - 109 999 => CALL_STATUS\r\n- 110 000 - 119 999 => IVR\r\n- 120 000 - 129 999 => ROUTING\r\n- 130 000 - 139 999 => AGENT_ACTION\r\n- 140 000 - 149 999 => TRANSFER\r\n- 150 000 - 159 999 => RM_INTEGRATION\r\n- 160 000 - 169 999 => WFM_INTEGRATION\r\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\r\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\r\n\r\n### Registering Twilio Actions\r\n\r\nIt is important to log information from the Twilio Flex UI for all implementations even when there is little customization happening. Many developers find it useful to generate log activity when some, or any, of the Twilio Actions occur within the Twilio Flex UI. Twilio Actions are events that arise from the Twilio Flex UI base implementation and indicate when certain activity is occurring - such as an agent hanging up a call. The CLE Logger library can be setup to automatically register all, or selected, Twilio Actions such that right after any code tied to the Action event has been executed a log statement is written indicating the Action occurred. This log statement will also automatically include the action event payload. To enable this logging capability, use the following approach to register the Twilio Actions. This only needs to be performed once during the initialization of the Twilio Flex UI page on the client.\r\n\r\nExample in a Twilio Flex UI plugin:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\n/**\r\n * @param flex { typeof import('@twilio/flex-ui') }\r\n * @param manager { import('@twilio/flex-ui').Manager }\r\n */\r\ninit(flex, manager) {\r\n\tcleLogger.init({\r\n\t\taccountSid: \"your-account-sid\"\r\n\t});\r\n\r\n\tcleLogger.registerTwilioActions(flex);\r\n}\r\n```\r\n\r\n**Note**: A Twilio account sid must be provided during `init` with the `accountSid` parameter, otherwise the actions won't be logged.\r\n\r\nThe logger will loop through all actions found under `flex.Actions.actions`, and attach `after` event handlers to log the action name and payload. Any private properties found in the payload (starting with '\\_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Not all Action events need to be logged and by using the `excludedFlexActionNames` property in the logger configuration the Action events not desired can be ignored. Any actions names configured in `excludedFlexActionNames` will not be included in the logs.\r\n\r\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\r\n\r\n**Twilio Actions log levels**\r\n\r\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of `INFO`.\r\n\r\n```js\r\nAcceptTask: INFO;\r\nCancelTransfer: INFO;\r\nCompleteTask: INFO;\r\nHangupCall: INFO;\r\nHideDirectory: VERBOSE;\r\nHistoryGo: VERBOSE;\r\nHistoryGoBack: VERBOSE;\r\nHistoryGoForward: VERBOSE;\r\nHistoryPush: VERBOSE;\r\nHistoryReplace: VERBOSE;\r\nHoldCall: INFO;\r\nHoldParticipant: INFO;\r\nKickParticipant: INFO;\r\nLogout: INFO;\r\nMonitorCall: INFO;\r\nNavigateToView: INFO;\r\nRejectTask: INFO;\r\nSelectTask: INFO;\r\nSelectTaskInSupervisor: INFO;\r\nSelectWorkerInSupervisor: INFO;\r\nSendMessage: INFO;\r\nSendTyping: VERBOSE;\r\nSetActivity: INFO;\r\nSetInputText: VERBOSE;\r\nShowDirectory: VERBOSE;\r\nStopMonitoringCall: INFO;\r\nToggleMute: INFO;\r\nToggleSidebar: VERBOSE;\r\nTransferTask: INFO;\r\nUnholdCall: INFO;\r\nUnholdParticipant: INFO;\r\nWrapupTask: INFO;\r\n```\r\n\r\n### Twilio Event Hooks\r\n\r\nCurrently, the Twilio console only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with `twilioEventHooks` option, and use the following functions to send event data to a custom endpoint.\r\n\r\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is `application/x-www-form-urlencoded` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// unrelated init properties omitted from this example.\r\n\t// include in the twilioEventHooks array an entry for only the event hooks\r\n\t// the executing code is going to relay. For example, if the code is only\r\n\t// handling task router web hook events then you would only need to define\r\n\t// the taskRouter relay url here.\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\r\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\r\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\r\n\t},\r\n});\r\n\r\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\r\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\r\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\r\n```\r\n\r\nExample Azure Function hook for debugger events:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\nmodule.exports = async function(context, req) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\r\n\t\t},\r\n\t});\r\n\r\n\t// log to console\r\n\tcleLogger.info(\"Received Twilio debugger event\");\r\n\r\n\t// send event data to a configured url\r\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\r\n};\r\n```\r\n\r\n**NOTE WHEN USING THIS FUNCTIONALITY IN TWILIO FUNCTIONS:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to `x-www-form-urlencoded` format.\r\n\r\nExample Twilio function task router relay using the `form-urlencoded` npm package to convert the data to `x-www-form-urlencoded` format:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\nconst formurlencoded = require(\"form-urlencoded\").default;\r\n\r\nexports.handler = async function(context, event, callback) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\r\n\t\t},\r\n\t});\r\n\r\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\r\n\r\n\tcallback();\r\n};\r\n```\r\n\r\n### Version\r\n\r\nVersion will be automatically included in all logs.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.version; // 1.0.0\r\n```\r\n\r\n## Log Object\r\n\r\nThe following is the type definition of the log object received by [log sinks](#log-sinks). It consists of properties provided in log messages, as well as some that are automatically inserted like the `eventDate`. You can use this information to customize your logs inside a custom log sink (e.g. filter out unwanted logs, modify some of the properties to include more data, or any kind of conditional logic based on the value of these properties). If using the built in `remoteLogger` log processor with your custom backend, you can expect an array of these objects to be sent to your log endpoint. For more info about implementing a custom logging endpoint see the [remote logging](#remote-logging) section.\r\n\r\n```js\r\n{\r\n\t// Account sid if provided during init\r\n\taccountSid: string;\r\n\r\n\t// Any additional log data to be included with the log\r\n\tadditionalData: unknown;\r\n\r\n\t// Component name, if provided\r\n\tcomponentName: string;\r\n\r\n\t// Generated or configured token\r\n\tcorrelationToken: string;\r\n\r\n\t// Current UTC datetime in ISO 8601 format\r\n\teventDate: string;\r\n\r\n\t// Log level\r\n\tlevel: string;\r\n\r\n\t// Log code, if provided\r\n\tlogCode: number;\r\n\r\n\t// Log message\r\n\tmessage: string;\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\tproperties: object;\r\n\r\n\t// BrowserFunction if the environment is the browser\r\n\t// CloudFunction if the environment is Node.js\r\n\tsource: string;\r\n\r\n\t// Indicates that the log contains PII data\r\n\tcontainsPII: boolean;\r\n\r\n\t// Logging library version\r\n\tloggerVersion: string;\r\n}\r\n```\r\n\r\n## Handling PII Data\r\n\r\nThe logger provides support for helping to protect log messages or data that need to include PII (Personally Identifiable Information). While the logger has several mechanisms that can be used to flag logs that contain PII and block PII from going to particular log sinks, it is still ultimately the developers responsibility to ensure PII is flagged or marked appropriately for the logger to know when it exists - the logger cannot automatically detect if PII is part of a log statement or data object. The general guideline is that developers should avoid including any PII data in log information whenever possible. In the instances where PII data must be included there are several mechanisms the logger supports to help indicate PII data and protect where it is sent.\r\n\r\nA `containsPII` parameter exists on log statement methods. This flag should be set to true if there is PII data, or any potential chance of it, in the log message, additional data or custom properties data. When this flag is set to true then it will only allow log sinks that have the `piiSafe` configuration set to true to receive and process the log message. `piiSafe` indicates on a log sink whether that log sink receiver is able to internally store PII data OR has logic to strip PII data that is tagged in the message or from pre-configured additional data fields before the log is written to any storage or output device. When sending data to any `remoteLogger` endpoint the log sink can be marked `piiSafe` only as long as data is appropriately tagged in messages and/or the names of custom properties that might have PII values in additional data are pre-configured AND the endpoint implementer has verified and ensured their receiving code will remove marked PII data immediately before sending logs to any output or storage media.\r\n\r\nTo tag any part of the message parameter in a log statement as containing PII within the actual log message use the `tagPII` utility. The `tagPII` utility will indicate to a remote backend that the text between the tags should be stripped or masked before sending logs to any output or storage media.\r\n\r\nExample tagged output:\r\n\r\n```js\r\nimport { tagPII } from \"@prftesp/cle-logger\";\r\n\r\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\r\n```\r\n\r\n## Advanced Configuration\r\n\r\n### Remote logging\r\n\r\nThe library provides a built in way to transmit logs to a remote http endpoint using the `remoteLogger` logs processor. The `remoteLogger` can be directed at a known listener such as CLE or you can create your own custom log listener. In addition, the log level in effect can be controlled remotely setting the `minLogLevelOverrideUrl` to an http address that will return the current log level to use instead of any defined default logLevel on each `logSink`. The `minLogLevelOverrideUrl` can point to a known central API such as CLE or you can create your own endpoint.\r\n\r\nThe following sections outline how to create your own endpoints for either receiving log messages from the `remoteLogger` log sink or providing the current log level override to it.\r\n\r\n**Remote log endpoint**\r\n\r\nTo implement your own remote logging endpoint, all you have to do is accept `POST` requests, with a JSON payload of an array of [log objects](#log-object), and return either `{ success: true }` or `{ success: false }`. The endpoint should always return a `200 OK` status even if your server code fails so that the CLE logger does not cause failures in the business logic in which it is embedded. A good use-case would be leveraging a serverless api platform to output logs to a queue.\r\n\r\nExample of an Azure Function that can receive requests from the `remoteLogger` log sink:\r\n\r\n```js\r\nmodule.exports = async function(context, req) {\r\n\tcontext.log(req.body); // body will have the log array\r\n\r\n\tcontext.res = {\r\n\t\tstatus: 200,\r\n\t\tbody: { success: true },\r\n\t};\r\n};\r\n```\r\n\r\n**Remote log level override**\r\n\r\nThe `minLogLevelOverrideUrl` configuration property is used to provide an endpoint that accepts `GET` requests and a `accountSid` query string parameter. `accountSid` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the `minLogLevel` configuration property. If null is returned, the `minLogLevel` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a `200 OK` status code and use the `success` property to indicate failure.\r\n\r\nExample success response:\r\n\r\n```json\r\n{\r\n\t\"success\": true,\r\n\t\"minLogLevel\": \"warn\"\r\n}\r\n```\r\n\r\nExample error response:\r\n\r\n```json\r\n{\r\n\t\"success\": false,\r\n\t\"minLogLevel\": null\r\n}\r\n```\r\n\r\n## Changelog\r\n\r\n### v2.0.6\r\n\r\n**New features**\r\n\r\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the call sid during a call, and automatically include it in `customProperties` log property for Flex Actions that happen during that call.\r\n\r\n### v2.0.5\r\n\r\n**New features**\r\n\r\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the worker sid and automatically include it in `customProperties` log property.\r\n\r\n**Other updates**\r\n\r\n- Updated documentation based on user feedback.\r\n\r\n### v2.0.4\r\n\r\n**Bug fixes**\r\n\r\n- Fixed issue with logging to console in Twilio Functions. The `consoleLogger` will now output the log object as serialized json.\r\n\r\n### v2.0.0 [2019-12-20]\r\n\r\n**New features**\r\n\r\n- All logging output is now configured using the `logSinks` property. The library provides two commonly used loggers - `consoleLogger` and a `remoteLogger` that accepts the log url as a parameter. [Configuration](#configuration)\r\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\r\n- Log functions have a new optional `containsPII` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\r\n- A new helper function `tagPII` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\r\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\r\n\r\n**Breaking changes**\r\n\r\n- `customLogSink` configuration renamed to `logSinks`\r\n- Removed `disableRemoteLogging` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\r\n- Removed `logToConsole` configuration. This is now configured as a log sink with a `consoleLogger` helper function.\r\n- Removed `logUrl` configuration. This is now configured as a log sink with a `remoteLogger` helper function.\r\n- Removed `minLogLevel` configuration. This is now configured per log sink.\r\n\r\n**Migration guide**\r\n\r\n```js\r\nimport cleLogger from \"@prftesp/cle-logger\";\r\n\r\n// v1.0.0 and older config\r\ncleLogger.init({\r\n\tdisableRemoteLogSinks: true, // no direct replacement\r\n\tlogToConsole: true, // replaced by the \"console logger\" sink\r\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\r\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\r\n\tcustomLogSinks: [\r\n\t\t{\r\n\t\t\tname: \"custom sink\",\r\n\t\t\tlog: logs => {\r\n\t\t\t\t/* your custom logging */\r\n\t\t\t},\r\n\t\t},\r\n\t],\r\n});\r\n\r\n// v2.0.0 config\r\ncleLogger.init({\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\tname: \"custom sink\",\r\n\t\t\tlog: logs => {\r\n\t\t\t\t/* your custom logging */\r\n\t\t\t},\r\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t\t// set to true to get feature parity with older version,\r\n\t\t\t// please consider your use case before setting\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"console logger\",\r\n\t\t\tlog: consoleLogger,\r\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t\t// set to true to get feature parity with older version,\r\n\t\t\t// please consider your use case before setting\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"remote logger\",\r\n\t\t\tlog: remoteLogger(\"http://log.output/api\"),\r\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t\t// set to true to get feature parity with older version,\r\n\t\t\t// please consider your use case before setting\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t],\r\n});\r\n```\r\n\r\n### v1.0.0 [2019-09-12]\r\n\r\n- Initial release\r\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.0.6-beta.2","_nodeVersion":"10.15.2","_npmVersion":"6.11.3","dist":{"integrity":"sha512-fVHnD7ixJMEYexECTu6td0L1VWlozO0cBfaN6F0F/+uyTwiVRBD8BOo0dTH4u22aWVgmG2lqHWQXLHiNSUf+Mw==","shasum":"8940b244b2272f36736a80a00bb54fb146b1ffbd","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.0.6-beta.2.tgz","fileCount":17,"unpackedSize":78606,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeT9ShCRA9TVsSAnZWagAAF+sP/0oYF3FjfyVU/I+bLs7u\nyEJXeI+EYQZGSXgFsiG8tsTTzk/KJ401b3fTqovU6MZtpk2rZyxhJnLMoSYF\n4viicjsuECNq4pcpRXmlIziDJ9fo4Rw1zvXpbuQfXTvMxnJCkxEEy5Y8bVSn\nj/8DCGy+xcaPZv9Zc7cDzArzb3HX+VYzH83jd3taw7bMrBwxQqThwyHHITD6\n9yy9ITZ0003DbQUgtNnTnV8mK76Qx+0l8XbAZEiHoHtD/RtBLFzB2DwTxSF8\n6wJIq0CzTqzhGBC9ExssSBtBrcDUUvdhhnUeuVPCBm40/vc+0jlxMmU/0z8r\nkZOoCbfj4Ix4m1dRmQ7yVyoVXwfdKDzFaDNcBfsZYaWabZSFgA/f3zyjZ9u+\n6/130VyvHNNtVSpbSiKt3rlRDGQoxO00NR0QXMNdcQFUAmq2l/nAyRA9XppP\nYGtqx/iR2/rTpKdqV7WC6uQt7r3Avsfv8o3d22sEVzRGvoMwp2ygWNBm8aSa\n+CYWpNJIA238ehqpsWbqGdM340uqcgflh6e0TMhRX+bgGacJC7JniuHRyC+U\n9yA0UTiacqsHx1gwzz8WOTW6vu3hpW2exR4S6vEVMoDljSvP+NSoKiO+mthZ\nBXC5HTg/UkCJV4TI/9BwWiWFNS+yrbWFoQcAdq96OKaoXFXUd91vvFDnWjmo\noEjK\r\n=7kyX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGYpmJnJbwCApw4tTLGFVbt0P0U7qyDyTF90jgtR/GmKAiEAqG7uS5Kmm1xTChGDoZBU597/NumD7OykgbuHYRNoaVo="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.0.6-beta.2_1582290080744_0.18948594555989895"},"_hasShrinkwrap":false},"2.0.6-beta.20200221.2":{"name":"@prftesp/cle-logger","version":"2.0.6-beta.20200221.2","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿# CLE Logger\n\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\n\n## Documentation\n\n- [Target Environment](#target-environment)\n- [Simple Example](#simple-example)\n- [Installation](#installation)\n- [Features](#features)\n- [Configuration](#configuration)\n- [API](#api)\n  - [Log sinks](#log-sinks)\n  - [Logging statements](#logging-statements)\n  - [Logging parameters](#logging-parameters)\n  - [Logging constants](#logging-constants)\n  - [Log codes](#log-codes)\n  - [Registering Twilio Actions](#registering-twilio-actions)\n  - [Twilio Event Hooks](#twilio-event-hooks)\n  - [Version](#version)\n- [Log Object](#log-object)\n- [Handling PII Data](#handling-pii-data)\n- [Advanced Configuration](#advanced-configuration)\n  - [Remote logging](#remote-logging)\n- [Changelog](#changelog)\n\n## Target Environment\n\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\n\n## Simple Example\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"console sink\",\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\tpiiSafe: true,\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n});\n\n// will log message to console\ncleLogger.info(\"log message\");\n```\n\n## Installation\n\n```bash\n$ npm install @prftesp/cle-logger\n```\n\n## Features\n\n- Runs in Node.js and the browser\n- Remote logging\n- Multiple log sinks\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\n- Remote minimum log level overrides\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\n- Support for tagging logs with custom component names or correlation tokens\n- Send Twilio event hook data to a custom backend\n\n## Configuration\n\nTo configure the logger call the `init` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\n\n**Available options**\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// Twilio account sid, required for use with CLE\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\n\n\t// Will be associated with all logs,\n\t// if not provided, a unique uuid will be generated\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\n\n\t// An array of that will receive an array of log objects,\n\t// and can optionally return a promise. Please see the\n\t// \"Log Sinks\" section for more information.\n\tlogSinks: [\n\t\t{\n\t\t\t// name of the log sink\n\t\t\tname: \"exampleConsoleSink\",\n\t\t\t// log handler function\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\t// This flag defines if the log message is processed by this sink when the contains\n\t\t\t// PII parameter of the log statement is true. piiSafe of true indicates all log\n\t\t\t// messages are allowed (even when contains PII is true); piiSafe of false indicates\n\t\t\t// only log messages with contains PII set to false will be processed by this log sink.\n\t\t\t// Defaults to false\n\t\t\tpiiSafe: true,\n\t\t\t// log level for the sink, defaults to INFO\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n\n\t// A way to suppress error messages if set to false, defaults to true\n\tlogErrorsToConsole: true,\n\n\t// If logging Twilio Flex Actions, provide a list of actions\n\t// to be excluded from logs\n\texcludedFlexActionNames: [\"SendTyping\"],\n\n\t// A custom endpoint to fetch log level overrides\n\t// NOTE: the provided url should always end with 'accountSid=',\n\t// the library will append the accountSid\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\n\n\t// An instance of ('@twilio/flex-ui').Manager.\n\t// If provided, it will be used to get the worker sid and call sid (only during a call), and \n\t// automatically include it in 'customProperties' log property and with every log\n\ttwilioFlexManager: manager,\n\n\t// To send data from Twilio event hooks, to a custom backend,\n\t// configure one or more endpoints for each event type\n\ttwilioEventHooks: {\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\n\t},\n});\n```\n\n## API\n\nThe logger can be imported as an object in CommonJs or ES6 syntax:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n// or\nconst cleLogger = require(\"@prftesp/cle-logger\");\n```\n\nAlternatively, all top level members of the `cleLogger` are available as named exports and can be imported separately:\n\n```js\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\n\ninit({\n\tlogSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: logLevels.VERBOSE\n\t}],\n});\n\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\n\n// vs\n\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ninit({\n\tcleLogger.logSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: cleLogger.consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}],\n});\n\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\n```\n\n### Log sinks\n\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the `logSinks` config. The logSinks configuration defines an array of objects with each object defining the log sink activity. All provided log sinks will be executed in parallel. A log sink configuration must include the following properties:\n\n- `name`: this is an identifiable name for the logger and must be unique per log sink defined\n- `log`: this indicates the logic to process on each batch of logs processed by the sink. There are several built in processing functions that can be used or you can insert any code inline with this definition. The built in processing functions are `consoleLogger` which will send the log statements to the attached console or `remoteLogger` which will send the log statements to the specified url provided as a definition on the parameter (i.e. `remoteLogger(\"https://mylogger.url\"`). For a custom log processing implementation, the logic must be async operations and must return a Promise object. The custom log method will receive an array of log objects for you to handle. You can find out more about the log object type in the [Log Object](#log-object) section. Here is an example of a custom log function:\n\n```js\nlogs => {\n\t// log each received log to the console separately\n\tlogs.forEach(log => console.log(log));\n\treturn Promise.resolve();\n};\n```\n\n- `logLevel`: defines the minimum default log level that will be processed by this log sink. Log levels lower than the minimum level will not be processed by this log sink. The minimum default log level can be overridden at run time by the value returned from the overall `minLogLevelOverrideUrl` that is set up in the library `init`.\n- `piiSafe`: indicates if log messages with a `containsPII` parameter value of true should be processed by the log sink. If `containsPII` parameter value on the log message is true and piiSafe is false then the log message will not be processed by this log sink, if piiSafe is true then it will. If the `containsPII` parameter value is false on the log message than it is always processed by the log sink regardless of the piiSafe value.\n\nCustom log sink configuration example. In this example, the custom code executed for each log statement is to append the log into a file. Note how a Promise is returned as required:\n\n```js\n{\n\t// name of the log sink\n\tname: \"exampleConsoleSink\",\n\t// log handler function - this is where you put your custom handling code and\n\t// remember to always return a promise. If your operation is not asynchronous,\n\t// you can do this by returning Promise.resolve().\n\tlog: logs => {\n\t\tfs.appendFile(\n\t\t\t\"./log.txt\",\n\t\t\t`${JSON.stringify(logs)}`,\n\t\t\tconsole.log\n\t\t);\n\t\treturn Promise.resolve();\n\t},\n\t// should this log sink accept PII data, defaults to false\n\tpiiSafe: true,\n\t// log level for the sink, defaults to INFO\n\tlogLevel: cleLogger.logLevels.VERBOSE\n}\n```\n\nExample log sink configuration that uses a custom coded sink and both standard console and remote logging sinks:\n\n```js\nconst fs = require(\"fs\");\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"fileSink\",\n\t\t\tlog: logs => {\n\t\t\t\tfs.appendFile(\"./log.txt\", `${JSON.stringify(logs)}`, console.log);\n\t\t\t\treturn Promise.resolve();\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"remoteSink\",\n\t\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\n\t\t\tlogLevel: cleLogger.logLevels.WARN,\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"consoleSink\",\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n});\n```\n\n### Logging statements\n\nTo create logs, you can use one of the logging function available. They all take the same arguments, described below, and log at a corresponding log level. For example, if you use the `cleLogger.verbose` logging function, and your log sink is configured with `logLevel: cleLogger.logLevels.VERBOSE`, that log sink will receive the log. If your log sink is configured with a higher log level, for example `logLevel: cleLogger.logLevels.INFO`, then it would not receive the verbose log. \n\nHere are the available logging functions:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(...);\ncleLogger.info(...);\ncleLogger.warn(...);\ncleLogger.error(...);\ncleLogger.critical(...);\n```\n\nThe functions are all async, return `Promise<void>`, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\n\n### Logging parameters\n\nAll logging functions take the same parameters:\n\n- `message`: the only required property, no logs will be generated without it\n  - type: string\n  - required: yes\n- `additionalData`: any additional log data you would like to include with the log, can be any data type\n  - type: any\n  - required: no\n  - default value: null\n- `customProperties`: a single level deep object of important properties you would like to distinguish from general log data, like various sids CLE uses it to index these properties in Application Insights. For CLE, try to use keys found in `cleLogger.customPropertyKeys`\n  - type: object\n  - required: no\n  - default value: {}\n- `componentName`: another useful way to 'tag' your logs for easier searching. Can be any string, but for CLE, try to use available options in `cleLogger.componentNames`\n  - type: string\n  - required: no\n  - default value: \"\"\n- `logCode`: log code, can be any number used to categorize logs. For defined ranges see the [log codes](#log-codes) section.\n  - type: number\n  - required: no\n  - default value: 0\n- `correlationToken`: correlation token to be associated with this log only\n  - type: string\n  - required: no\n  - default value: token provided in `init`, or a generated token if one wasn't provided\n- `containsPII`: does the log contain PII data, defaults to false. If set to true, the log will be passed in only to those log sinks that have `piiSafe` set to true. For more info see the [handling PII data](#handling-pii-data) section.\n  - type: boolean\n  - required: no\n  - default value: false\n\nExample verbose logging statement:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(\n\t\"Log message\", // message\n\t{ data: \"test\" }, // additionalData\n\t{ [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" }, // customProperties\n\tcleLogger.componentNames.SESSION, // componentName\n\t70123, // logCode\n\t\"my-token\", // correlationToken\n\ttrue, // containsPII\n);\n```\n\n### Logging constants\n\n**Logging levels**\n\nLogging level constants should be used when configuring the log level of a [log sink](#log-sinks).\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.logLevels.VERBOSE; // verbose\ncleLogger.logLevels.INFO; // info\ncleLogger.logLevels.WARN; // warn\ncleLogger.logLevels.ERROR; // error\ncleLogger.logLevels.CRITICAL; // critical\n```\n\n**Component names**\n\nDefines a set of commonly used components that should be used for the `componentName` parameter value of a log statement if you are logging from one of these common components in a Twilio implementation.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.componentNames.SESSION;\ncleLogger.componentNames.DIALER;\ncleLogger.componentNames.DASHBOARD;\ncleLogger.componentNames.AFTERCALL;\ncleLogger.componentNames.SUPERVISOR;\n```\n\n**Custom property keys**\n\nDefines a set of commonly used key that should be used when defining the custom properties object that is supplied in the `customProperties` parameter of any logging statement.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.customPropertyKeys.CHANNEL_SID;\ncleLogger.customPropertyKeys.TASK_SID;\ncleLogger.customPropertyKeys.CALL_SID;\ncleLogger.customPropertyKeys.WORKFLOW_SID;\ncleLogger.customPropertyKeys.WORKER_SID;\n```\n\n### Log codes\n\nIt can be helpful when investigating issues to only view logs related to specific areas of a system. Use Log Codes to give a category definition for what area of the contact center the log activity occurred within. Use numbers within the following pre-defined log code ranges or define custom log codes by using your own numbers higher than 300,000.\n\n- 100 000 - 109 999 => CALL_STATUS\n- 110 000 - 119 999 => IVR\n- 120 000 - 129 999 => ROUTING\n- 130 000 - 139 999 => AGENT_ACTION\n- 140 000 - 149 999 => TRANSFER\n- 150 000 - 159 999 => RM_INTEGRATION\n- 160 000 - 169 999 => WFM_INTEGRATION\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\n\n### Registering Twilio Actions\n\nIt is important to log information from the Twilio Flex UI for all implementations even when there is little customization happening. Many developers find it useful to generate log activity when some, or any, of the Twilio Actions occur within the Twilio Flex UI. Twilio Actions are events that arise from the Twilio Flex UI base implementation and indicate when certain activity is occurring - such as an agent hanging up a call. The CLE Logger library can be setup to automatically register all, or selected, Twilio Actions such that right after any code tied to the Action event has been executed a log statement is written indicating the Action occurred. This log statement will also automatically include the action event payload. To enable this logging capability, use the following approach to register the Twilio Actions. This only needs to be performed once during the initialization of the Twilio Flex UI page on the client.\n\nExample in a Twilio Flex UI plugin:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n/**\n * @param flex { typeof import('@twilio/flex-ui') }\n * @param manager { import('@twilio/flex-ui').Manager }\n */\ninit(flex, manager) {\n\tcleLogger.init({\n\t\taccountSid: \"your-account-sid\"\n\t});\n\n\tcleLogger.registerTwilioActions(flex);\n}\n```\n\n**Note**: A Twilio account sid must be provided during `init` with the `accountSid` parameter, otherwise the actions won't be logged.\n\nThe logger will loop through all actions found under `flex.Actions.actions`, and attach `after` event handlers to log the action name and payload. Any private properties found in the payload (starting with '\\_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Not all Action events need to be logged and by using the `excludedFlexActionNames` property in the logger configuration the Action events not desired can be ignored. Any actions names configured in `excludedFlexActionNames` will not be included in the logs.\n\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\n\n**Twilio Actions log levels**\n\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of `INFO`.\n\n```js\nAcceptTask: INFO;\nCancelTransfer: INFO;\nCompleteTask: INFO;\nHangupCall: INFO;\nHideDirectory: VERBOSE;\nHistoryGo: VERBOSE;\nHistoryGoBack: VERBOSE;\nHistoryGoForward: VERBOSE;\nHistoryPush: VERBOSE;\nHistoryReplace: VERBOSE;\nHoldCall: INFO;\nHoldParticipant: INFO;\nKickParticipant: INFO;\nLogout: INFO;\nMonitorCall: INFO;\nNavigateToView: INFO;\nRejectTask: INFO;\nSelectTask: INFO;\nSelectTaskInSupervisor: INFO;\nSelectWorkerInSupervisor: INFO;\nSendMessage: INFO;\nSendTyping: VERBOSE;\nSetActivity: INFO;\nSetInputText: VERBOSE;\nShowDirectory: VERBOSE;\nStopMonitoringCall: INFO;\nToggleMute: INFO;\nToggleSidebar: VERBOSE;\nTransferTask: INFO;\nUnholdCall: INFO;\nUnholdParticipant: INFO;\nWrapupTask: INFO;\n```\n\n### Twilio Event Hooks\n\nCurrently, the Twilio console only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with `twilioEventHooks` option, and use the following functions to send event data to a custom endpoint.\n\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is `application/x-www-form-urlencoded` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// unrelated init properties omitted from this example.\n\t// include in the twilioEventHooks array an entry for only the event hooks\n\t// the executing code is going to relay. For example, if the code is only\n\t// handling task router web hook events then you would only need to define\n\t// the taskRouter relay url here.\n\ttwilioEventHooks: {\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\n\t},\n});\n\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\n```\n\nExample Azure Function hook for debugger events:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\nmodule.exports = async function(context, req) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\t},\n\t});\n\n\t// log to console\n\tcleLogger.info(\"Received Twilio debugger event\");\n\n\t// send event data to a configured url\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\n};\n```\n\n**NOTE WHEN USING THIS FUNCTIONALITY IN TWILIO FUNCTIONS:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to `x-www-form-urlencoded` format.\n\nExample Twilio function task router relay using the `form-urlencoded` npm package to convert the data to `x-www-form-urlencoded` format:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\nconst formurlencoded = require(\"form-urlencoded\").default;\n\nexports.handler = async function(context, event, callback) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\t},\n\t});\n\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\n\n\tcallback();\n};\n```\n\n### Version\n\nVersion will be automatically included in all logs.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.version; // 1.0.0\n```\n\n## Log Object\n\nThe following is the type definition of the log object received by [log sinks](#log-sinks). It consists of properties provided in log messages, as well as some that are automatically inserted like the `eventDate`. You can use this information to customize your logs inside a custom log sink (e.g. filter out unwanted logs, modify some of the properties to include more data, or any kind of conditional logic based on the value of these properties). If using the built in `remoteLogger` log processor with your custom backend, you can expect an array of these objects to be sent to your log endpoint. For more info about implementing a custom logging endpoint see the [remote logging](#remote-logging) section.\n\n```js\n{\n\t// Account sid if provided during init\n\taccountSid: string;\n\n\t// Any additional log data to be included with the log\n\tadditionalData: unknown;\n\n\t// Component name, if provided\n\tcomponentName: string;\n\n\t// Generated or configured token\n\tcorrelationToken: string;\n\n\t// Current UTC datetime in ISO 8601 format\n\teventDate: string;\n\n\t// Log level\n\tlevel: string;\n\n\t// Log code, if provided\n\tlogCode: number;\n\n\t// Log message\n\tmessage: string;\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\tproperties: object;\n\n\t// BrowserFunction if the environment is the browser\n\t// CloudFunction if the environment is Node.js\n\tsource: string;\n\n\t// Indicates that the log contains PII data\n\tcontainsPII: boolean;\n\n\t// Logging library version\n\tloggerVersion: string;\n}\n```\n\n## Handling PII Data\n\nThe logger provides support for helping to protect log messages or data that need to include PII (Personally Identifiable Information). While the logger has several mechanisms that can be used to flag logs that contain PII and block PII from going to particular log sinks, it is still ultimately the developers responsibility to ensure PII is flagged or marked appropriately for the logger to know when it exists - the logger cannot automatically detect if PII is part of a log statement or data object. The general guideline is that developers should avoid including any PII data in log information whenever possible. In the instances where PII data must be included there are several mechanisms the logger supports to help indicate PII data and protect where it is sent.\n\nA `containsPII` parameter exists on log statement methods. This flag should be set to true if there is PII data, or any potential chance of it, in the log message, additional data or custom properties data. When this flag is set to true then it will only allow log sinks that have the `piiSafe` configuration set to true to receive and process the log message. `piiSafe` indicates on a log sink whether that log sink receiver is able to internally store PII data OR has logic to strip PII data that is tagged in the message or from pre-configured additional data fields before the log is written to any storage or output device. When sending data to any `remoteLogger` endpoint the log sink can be marked `piiSafe` only as long as data is appropriately tagged in messages and/or the names of custom properties that might have PII values in additional data are pre-configured AND the endpoint implementer has verified and ensured their receiving code will remove marked PII data immediately before sending logs to any output or storage media.\n\nTo tag any part of the message parameter in a log statement as containing PII within the actual log message use the `tagPII` utility. The `tagPII` utility will indicate to a remote backend that the text between the tags should be stripped or masked before sending logs to any output or storage media.\n\nExample tagged output:\n\n```js\nimport { tagPII } from \"@prftesp/cle-logger\";\n\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\n```\n\n## Advanced Configuration\n\n### Remote logging\n\nThe library provides a built in way to transmit logs to a remote http endpoint using the `remoteLogger` logs processor. The `remoteLogger` can be directed at a known listener such as CLE or you can create your own custom log listener. In addition, the log level in effect can be controlled remotely setting the `minLogLevelOverrideUrl` to an http address that will return the current log level to use instead of any defined default logLevel on each `logSink`. The `minLogLevelOverrideUrl` can point to a known central API such as CLE or you can create your own endpoint.\n\nThe following sections outline how to create your own endpoints for either receiving log messages from the `remoteLogger` log sink or providing the current log level override to it.\n\n**Remote log endpoint**\n\nTo implement your own remote logging endpoint, all you have to do is accept `POST` requests, with a JSON payload of an array of [log objects](#log-object), and return either `{ success: true }` or `{ success: false }`. The endpoint should always return a `200 OK` status even if your server code fails so that the CLE logger does not cause failures in the business logic in which it is embedded. A good use-case would be leveraging a serverless api platform to output logs to a queue.\n\nExample of an Azure Function that can receive requests from the `remoteLogger` log sink:\n\n```js\nmodule.exports = async function(context, req) {\n\tcontext.log(req.body); // body will have the log array\n\n\tcontext.res = {\n\t\tstatus: 200,\n\t\tbody: { success: true },\n\t};\n};\n```\n\n**Remote log level override**\n\nThe `minLogLevelOverrideUrl` configuration property is used to provide an endpoint that accepts `GET` requests and a `accountSid` query string parameter. `accountSid` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the `minLogLevel` configuration property. If null is returned, the `minLogLevel` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a `200 OK` status code and use the `success` property to indicate failure.\n\nExample success response:\n\n```json\n{\n\t\"success\": true,\n\t\"minLogLevel\": \"warn\"\n}\n```\n\nExample error response:\n\n```json\n{\n\t\"success\": false,\n\t\"minLogLevel\": null\n}\n```\n\n## Changelog\n\n### v2.0.6\n\n**New features**\n\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the call sid during a call, and automatically include it in `customProperties` log property for all logs that happen during that call (including Flex Action logs).\n\n### v2.0.5\n\n**New features**\n\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the worker sid and automatically include it in `customProperties` log property.\n\n**Other updates**\n\n- Updated documentation based on user feedback.\n\n### v2.0.4\n\n**Bug fixes**\n\n- Fixed issue with logging to console in Twilio Functions. The `consoleLogger` will now output the log object as serialized json.\n\n### v2.0.0 [2019-12-20]\n\n**New features**\n\n- All logging output is now configured using the `logSinks` property. The library provides two commonly used loggers - `consoleLogger` and a `remoteLogger` that accepts the log url as a parameter. [Configuration](#configuration)\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\n- Log functions have a new optional `containsPII` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\n- A new helper function `tagPII` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\n\n**Breaking changes**\n\n- `customLogSink` configuration renamed to `logSinks`\n- Removed `disableRemoteLogging` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\n- Removed `logToConsole` configuration. This is now configured as a log sink with a `consoleLogger` helper function.\n- Removed `logUrl` configuration. This is now configured as a log sink with a `remoteLogger` helper function.\n- Removed `minLogLevel` configuration. This is now configured per log sink.\n\n**Migration guide**\n\n```js\nimport cleLogger from \"@prftesp/cle-logger\";\n\n// v1.0.0 and older config\ncleLogger.init({\n\tdisableRemoteLogSinks: true, // no direct replacement\n\tlogToConsole: true, // replaced by the \"console logger\" sink\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\n\tcustomLogSinks: [\n\t\t{\n\t\t\tname: \"custom sink\",\n\t\t\tlog: logs => {\n\t\t\t\t/* your custom logging */\n\t\t\t},\n\t\t},\n\t],\n});\n\n// v2.0.0 config\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"custom sink\",\n\t\t\tlog: logs => {\n\t\t\t\t/* your custom logging */\n\t\t\t},\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"console logger\",\n\t\t\tlog: consoleLogger,\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"remote logger\",\n\t\t\tlog: remoteLogger(\"http://log.output/api\"),\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t],\n});\n```\n\n### v1.0.0 [2019-09-12]\n\n- Initial release\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.0.6-beta.20200221.2","_nodeVersion":"12.16.0","_npmVersion":"6.13.7","dist":{"integrity":"sha512-bv4cgp0hev+9oY3E/ytUkqWKMfBfrF/aRsMG0poduuEa3WfcBfpChkSmZi9hOXRYC7asd7+I8nktKD4i4NW2ug==","shasum":"129aa192aecfc5a8d9e08a1f93f1afc279002aea","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.0.6-beta.20200221.2.tgz","fileCount":17,"unpackedSize":77124,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeT/J2CRA9TVsSAnZWagAAr84P/0qBSL+57aelxPT5SeBV\nW0K/JAsarROkvng9JMUcm0ZPGX8marsjMT6ZKxIMx3zU07fctSp2XHMcXBQG\nP5dE02+o8p0mNHAK1dSVRwYvCL6R4NUVc65BMIwKKcxwBwW5I8KEO8whh8dc\n36+hJtrV7LctTEe3La3V7VeychKgZfc6Vrt0nvScn3XXTry+XfbNLnCIqM3R\nR1IRxb1dXLvkIx5US6cSHxMr9oaJzPUf+bZeTlgtrwFy+q0J5cvV92+jpv1x\ngtUHcyPGQu5XaDPfCTbBZqZG2y0OD+GS84/O14yqxkPuy0CqgKCyA6p92Sd6\n5CG871jsiC5JOVEhxbacqCvzHwusrBudbBYTVD9zVN9UKeQPsjPzyk8//yLy\nWJC3NZEPseid/fbNpVjGsOS6JZDTK1b2JYsCYBOJ+566u1JNg8iWITiXw0VF\nDHWomD9W7+Dw0bCubtD3JLdxDTvQ7Qogsszknou1PQD635/66CZDxzwUyDjx\npH5++rdVDy6MZ4r2vA7/bNzAzCAloF/RSIvHsQCE6H9repY7C0KSmGr9wgeT\nr+e2FDz3VP9Rdd37Gb29WtyiE+Lz8Yz0WKZzRLgcdW2Kp6sX59yXKSb/EJT4\nsrwjKGbSs0NKrRlJfWDSmUYhesgHBZvjMsA+vEHZbUlgRGjTq4oE+ZwHFKPp\nRM0j\r\n=8AG3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDm/9Cjbx69p6KBsx2VOh5uTjvNmzINwicUuNNdPtIGRwIhAN8BRh21FaEAg4xB1KycJz3vQXWT0hzj9mtFH72ArNUg"}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.0.6-beta.20200221.2_1582297717674_0.21275087615678556"},"_hasShrinkwrap":false},"2.0.7-beta.20200221.4":{"name":"@prftesp/cle-logger","version":"2.0.7-beta.20200221.4","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿# CLE Logger\n\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\n\n## Documentation\n\n- [Target Environment](#target-environment)\n- [Simple Example](#simple-example)\n- [Installation](#installation)\n- [Features](#features)\n- [Configuration](#configuration)\n- [API](#api)\n  - [Log sinks](#log-sinks)\n  - [Logging statements](#logging-statements)\n  - [Logging parameters](#logging-parameters)\n  - [Logging constants](#logging-constants)\n  - [Log codes](#log-codes)\n  - [Registering Twilio Actions](#registering-twilio-actions)\n  - [Twilio Event Hooks](#twilio-event-hooks)\n  - [Version](#version)\n- [Log Object](#log-object)\n- [Handling PII Data](#handling-pii-data)\n- [Advanced Configuration](#advanced-configuration)\n  - [Remote logging](#remote-logging)\n- [Changelog](#changelog)\n\n## Target Environment\n\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\n\n## Simple Example\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"console sink\",\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\tpiiSafe: true,\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n});\n\n// will log message to console\ncleLogger.info(\"log message\");\n```\n\n## Installation\n\n```bash\n$ npm install @prftesp/cle-logger\n```\n\n## Features\n\n- Runs in Node.js and the browser\n- Remote logging\n- Multiple log sinks\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\n- Remote minimum log level overrides\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\n- Support for tagging logs with custom component names or correlation tokens\n- Send Twilio event hook data to a custom backend\n\n## Configuration\n\nTo configure the logger call the `init` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\n\n**Available options**\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// Twilio account sid, required for use with CLE\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\n\n\t// Will be associated with all logs,\n\t// if not provided, a unique uuid will be generated\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\n\n\t// An array of that will receive an array of log objects,\n\t// and can optionally return a promise. Please see the\n\t// \"Log Sinks\" section for more information.\n\tlogSinks: [\n\t\t{\n\t\t\t// name of the log sink\n\t\t\tname: \"exampleConsoleSink\",\n\t\t\t// log handler function\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\t// This flag defines if the log message is processed by this sink when the contains\n\t\t\t// PII parameter of the log statement is true. piiSafe of true indicates all log\n\t\t\t// messages are allowed (even when contains PII is true); piiSafe of false indicates\n\t\t\t// only log messages with contains PII set to false will be processed by this log sink.\n\t\t\t// Defaults to false\n\t\t\tpiiSafe: true,\n\t\t\t// log level for the sink, defaults to INFO\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n\n\t// A way to suppress error messages if set to false, defaults to true\n\tlogErrorsToConsole: true,\n\n\t// If logging Twilio Flex Actions, provide a list of actions\n\t// to be excluded from logs\n\texcludedFlexActionNames: [\"SendTyping\"],\n\n\t// A custom endpoint to fetch log level overrides\n\t// NOTE: the provided url should always end with 'accountSid=',\n\t// the library will append the accountSid\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\n\n\t// An instance of ('@twilio/flex-ui').Manager.\n\t// If provided, it will be used to get the worker sid and call sid (only during a call), and \n\t// automatically include it in 'customProperties' log property and with every log\n\ttwilioFlexManager: manager,\n\n\t// To send data from Twilio event hooks, to a custom backend,\n\t// configure one or more endpoints for each event type\n\ttwilioEventHooks: {\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\n\t},\n});\n```\n\n## API\n\nThe logger can be imported as an object in CommonJs or ES6 syntax:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n// or\nconst cleLogger = require(\"@prftesp/cle-logger\");\n```\n\nAlternatively, all top level members of the `cleLogger` are available as named exports and can be imported separately:\n\n```js\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\n\ninit({\n\tlogSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: logLevels.VERBOSE\n\t}],\n});\n\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\n\n// vs\n\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ninit({\n\tcleLogger.logSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: cleLogger.consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}],\n});\n\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\n```\n\n### Log sinks\n\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the `logSinks` config. The logSinks configuration defines an array of objects with each object defining the log sink activity. All provided log sinks will be executed in parallel. A log sink configuration must include the following properties:\n\n- `name`: this is an identifiable name for the logger and must be unique per log sink defined\n- `log`: this indicates the logic to process on each batch of logs processed by the sink. There are several built in processing functions that can be used or you can insert any code inline with this definition. The built in processing functions are `consoleLogger` which will send the log statements to the attached console or `remoteLogger` which will send the log statements to the specified url provided as a definition on the parameter (i.e. `remoteLogger(\"https://mylogger.url\"`). For a custom log processing implementation, the logic must be async operations and must return a Promise object. The custom log method will receive an array of log objects for you to handle. You can find out more about the log object type in the [Log Object](#log-object) section. Here is an example of a custom log function:\n\n```js\nlogs => {\n\t// log each received log to the console separately\n\tlogs.forEach(log => console.log(log));\n\treturn Promise.resolve();\n};\n```\n\n- `logLevel`: defines the minimum default log level that will be processed by this log sink. Log levels lower than the minimum level will not be processed by this log sink. The minimum default log level can be overridden at run time by the value returned from the overall `minLogLevelOverrideUrl` that is set up in the library `init`.\n- `piiSafe`: indicates if log messages with a `containsPII` parameter value of true should be processed by the log sink. If `containsPII` parameter value on the log message is true and piiSafe is false then the log message will not be processed by this log sink, if piiSafe is true then it will. If the `containsPII` parameter value is false on the log message than it is always processed by the log sink regardless of the piiSafe value.\n\nCustom log sink configuration example. In this example, the custom code executed for each log statement is to append the log into a file. Note how a Promise is returned as required:\n\n```js\n{\n\t// name of the log sink\n\tname: \"exampleConsoleSink\",\n\t// log handler function - this is where you put your custom handling code and\n\t// remember to always return a promise. If your operation is not asynchronous,\n\t// you can do this by returning Promise.resolve().\n\tlog: logs => {\n\t\tfs.appendFile(\n\t\t\t\"./log.txt\",\n\t\t\t`${JSON.stringify(logs)}`,\n\t\t\tconsole.log\n\t\t);\n\t\treturn Promise.resolve();\n\t},\n\t// should this log sink accept PII data, defaults to false\n\tpiiSafe: true,\n\t// log level for the sink, defaults to INFO\n\tlogLevel: cleLogger.logLevels.VERBOSE\n}\n```\n\nExample log sink configuration that uses a custom coded sink and both standard console and remote logging sinks:\n\n```js\nconst fs = require(\"fs\");\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"fileSink\",\n\t\t\tlog: logs => {\n\t\t\t\tfs.appendFile(\"./log.txt\", `${JSON.stringify(logs)}`, console.log);\n\t\t\t\treturn Promise.resolve();\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"remoteSink\",\n\t\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\n\t\t\tlogLevel: cleLogger.logLevels.WARN,\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"consoleSink\",\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n});\n```\n\n### Logging statements\n\nTo create logs, you can use one of the logging function available. They all take the same arguments, described below, and log at a corresponding log level. For example, if you use the `cleLogger.verbose` logging function, and your log sink is configured with `logLevel: cleLogger.logLevels.VERBOSE`, that log sink will receive the log. If your log sink is configured with a higher log level, for example `logLevel: cleLogger.logLevels.INFO`, then it would not receive the verbose log. \n\nHere are the available logging functions:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(...);\ncleLogger.info(...);\ncleLogger.warn(...);\ncleLogger.error(...);\ncleLogger.critical(...);\n```\n\nThe functions are all async, return `Promise<void>`, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\n\n### Logging parameters\n\nAll logging functions take the same parameters:\n\n- `message`: the only required property, no logs will be generated without it\n  - type: string\n  - required: yes\n- `additionalData`: any additional log data you would like to include with the log, can be any data type\n  - type: any\n  - required: no\n  - default value: null\n- `customProperties`: a single level deep object of important properties you would like to distinguish from general log data, like various sids CLE uses it to index these properties in Application Insights. For CLE, try to use keys found in `cleLogger.customPropertyKeys`\n  - type: object\n  - required: no\n  - default value: {}\n- `componentName`: another useful way to 'tag' your logs for easier searching. Can be any string, but for CLE, try to use available options in `cleLogger.componentNames`\n  - type: string\n  - required: no\n  - default value: \"\"\n- `logCode`: log code, can be any number used to categorize logs. For defined ranges see the [log codes](#log-codes) section.\n  - type: number\n  - required: no\n  - default value: 0\n- `correlationToken`: correlation token to be associated with this log only\n  - type: string\n  - required: no\n  - default value: token provided in `init`, or a generated token if one wasn't provided\n- `containsPII`: does the log contain PII data, defaults to false. If set to true, the log will be passed in only to those log sinks that have `piiSafe` set to true. For more info see the [handling PII data](#handling-pii-data) section.\n  - type: boolean\n  - required: no\n  - default value: false\n\nExample verbose logging statement:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(\n\t\"Log message\", // message\n\t{ data: \"test\" }, // additionalData\n\t{ [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" }, // customProperties\n\tcleLogger.componentNames.SESSION, // componentName\n\t70123, // logCode\n\t\"my-token\", // correlationToken\n\ttrue, // containsPII\n);\n```\n\n### Logging constants\n\n**Logging levels**\n\nLogging level constants should be used when configuring the log level of a [log sink](#log-sinks).\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.logLevels.VERBOSE; // verbose\ncleLogger.logLevels.INFO; // info\ncleLogger.logLevels.WARN; // warn\ncleLogger.logLevels.ERROR; // error\ncleLogger.logLevels.CRITICAL; // critical\n```\n\n**Component names**\n\nDefines a set of commonly used components that should be used for the `componentName` parameter value of a log statement if you are logging from one of these common components in a Twilio implementation.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.componentNames.SESSION;\ncleLogger.componentNames.DIALER;\ncleLogger.componentNames.DASHBOARD;\ncleLogger.componentNames.AFTERCALL;\ncleLogger.componentNames.SUPERVISOR;\n```\n\n**Custom property keys**\n\nDefines a set of commonly used key that should be used when defining the custom properties object that is supplied in the `customProperties` parameter of any logging statement.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.customPropertyKeys.CHANNEL_SID;\ncleLogger.customPropertyKeys.TASK_SID;\ncleLogger.customPropertyKeys.CALL_SID;\ncleLogger.customPropertyKeys.WORKFLOW_SID;\ncleLogger.customPropertyKeys.WORKER_SID;\n```\n\n### Log codes\n\nIt can be helpful when investigating issues to only view logs related to specific areas of a system. Use Log Codes to give a category definition for what area of the contact center the log activity occurred within. Use numbers within the following pre-defined log code ranges or define custom log codes by using your own numbers higher than 300,000.\n\n- 100 000 - 109 999 => CALL_STATUS\n- 110 000 - 119 999 => IVR\n- 120 000 - 129 999 => ROUTING\n- 130 000 - 139 999 => AGENT_ACTION\n- 140 000 - 149 999 => TRANSFER\n- 150 000 - 159 999 => RM_INTEGRATION\n- 160 000 - 169 999 => WFM_INTEGRATION\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\n\n### Registering Twilio Actions\n\nIt is important to log information from the Twilio Flex UI for all implementations even when there is little customization happening. Many developers find it useful to generate log activity when some, or any, of the Twilio Actions occur within the Twilio Flex UI. Twilio Actions are events that arise from the Twilio Flex UI base implementation and indicate when certain activity is occurring - such as an agent hanging up a call. The CLE Logger library can be setup to automatically register all, or selected, Twilio Actions such that right after any code tied to the Action event has been executed a log statement is written indicating the Action occurred. This log statement will also automatically include the action event payload. To enable this logging capability, use the following approach to register the Twilio Actions. This only needs to be performed once during the initialization of the Twilio Flex UI page on the client.\n\nExample in a Twilio Flex UI plugin:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n/**\n * @param flex { typeof import('@twilio/flex-ui') }\n * @param manager { import('@twilio/flex-ui').Manager }\n */\ninit(flex, manager) {\n\tcleLogger.init({\n\t\taccountSid: \"your-account-sid\"\n\t});\n\n\tcleLogger.registerTwilioActions(flex);\n}\n```\n\n**Note**: A Twilio account sid must be provided during `init` with the `accountSid` parameter, otherwise the actions won't be logged.\n\nThe logger will loop through all actions found under `flex.Actions.actions`, and attach `after` event handlers to log the action name and payload. Any private properties found in the payload (starting with '\\_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Not all Action events need to be logged and by using the `excludedFlexActionNames` property in the logger configuration the Action events not desired can be ignored. Any actions names configured in `excludedFlexActionNames` will not be included in the logs.\n\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\n\n**Twilio Actions log levels**\n\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of `INFO`.\n\n```js\nAcceptTask: INFO;\nCancelTransfer: INFO;\nCompleteTask: INFO;\nHangupCall: INFO;\nHideDirectory: VERBOSE;\nHistoryGo: VERBOSE;\nHistoryGoBack: VERBOSE;\nHistoryGoForward: VERBOSE;\nHistoryPush: VERBOSE;\nHistoryReplace: VERBOSE;\nHoldCall: INFO;\nHoldParticipant: INFO;\nKickParticipant: INFO;\nLogout: INFO;\nMonitorCall: INFO;\nNavigateToView: INFO;\nRejectTask: INFO;\nSelectTask: INFO;\nSelectTaskInSupervisor: INFO;\nSelectWorkerInSupervisor: INFO;\nSendMessage: INFO;\nSendTyping: VERBOSE;\nSetActivity: INFO;\nSetInputText: VERBOSE;\nShowDirectory: VERBOSE;\nStopMonitoringCall: INFO;\nToggleMute: INFO;\nToggleSidebar: VERBOSE;\nTransferTask: INFO;\nUnholdCall: INFO;\nUnholdParticipant: INFO;\nWrapupTask: INFO;\n```\n\n### Twilio Event Hooks\n\nCurrently, the Twilio console only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with `twilioEventHooks` option, and use the following functions to send event data to a custom endpoint.\n\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is `application/x-www-form-urlencoded` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// unrelated init properties omitted from this example.\n\t// include in the twilioEventHooks array an entry for only the event hooks\n\t// the executing code is going to relay. For example, if the code is only\n\t// handling task router web hook events then you would only need to define\n\t// the taskRouter relay url here.\n\ttwilioEventHooks: {\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\n\t},\n});\n\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\n```\n\nExample Azure Function hook for debugger events:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\nmodule.exports = async function(context, req) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\t},\n\t});\n\n\t// log to console\n\tcleLogger.info(\"Received Twilio debugger event\");\n\n\t// send event data to a configured url\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\n};\n```\n\n**NOTE WHEN USING THIS FUNCTIONALITY IN TWILIO FUNCTIONS:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to `x-www-form-urlencoded` format.\n\nExample Twilio function task router relay using the `form-urlencoded` npm package to convert the data to `x-www-form-urlencoded` format:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\nconst formurlencoded = require(\"form-urlencoded\").default;\n\nexports.handler = async function(context, event, callback) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\t},\n\t});\n\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\n\n\tcallback();\n};\n```\n\n### Version\n\nVersion will be automatically included in all logs.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.version; // 1.0.0\n```\n\n## Log Object\n\nThe following is the type definition of the log object received by [log sinks](#log-sinks). It consists of properties provided in log messages, as well as some that are automatically inserted like the `eventDate`. You can use this information to customize your logs inside a custom log sink (e.g. filter out unwanted logs, modify some of the properties to include more data, or any kind of conditional logic based on the value of these properties). If using the built in `remoteLogger` log processor with your custom backend, you can expect an array of these objects to be sent to your log endpoint. For more info about implementing a custom logging endpoint see the [remote logging](#remote-logging) section.\n\n```js\n{\n\t// Account sid if provided during init\n\taccountSid: string;\n\n\t// Any additional log data to be included with the log\n\tadditionalData: unknown;\n\n\t// Component name, if provided\n\tcomponentName: string;\n\n\t// Generated or configured token\n\tcorrelationToken: string;\n\n\t// Current UTC datetime in ISO 8601 format\n\teventDate: string;\n\n\t// Log level\n\tlevel: string;\n\n\t// Log code, if provided\n\tlogCode: number;\n\n\t// Log message\n\tmessage: string;\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\tproperties: object;\n\n\t// BrowserFunction if the environment is the browser\n\t// CloudFunction if the environment is Node.js\n\tsource: string;\n\n\t// Indicates that the log contains PII data\n\tcontainsPII: boolean;\n\n\t// Logging library version\n\tloggerVersion: string;\n}\n```\n\n## Handling PII Data\n\nThe logger provides support for helping to protect log messages or data that need to include PII (Personally Identifiable Information). While the logger has several mechanisms that can be used to flag logs that contain PII and block PII from going to particular log sinks, it is still ultimately the developers responsibility to ensure PII is flagged or marked appropriately for the logger to know when it exists - the logger cannot automatically detect if PII is part of a log statement or data object. The general guideline is that developers should avoid including any PII data in log information whenever possible. In the instances where PII data must be included there are several mechanisms the logger supports to help indicate PII data and protect where it is sent.\n\nA `containsPII` parameter exists on log statement methods. This flag should be set to true if there is PII data, or any potential chance of it, in the log message, additional data or custom properties data. When this flag is set to true then it will only allow log sinks that have the `piiSafe` configuration set to true to receive and process the log message. `piiSafe` indicates on a log sink whether that log sink receiver is able to internally store PII data OR has logic to strip PII data that is tagged in the message or from pre-configured additional data fields before the log is written to any storage or output device. When sending data to any `remoteLogger` endpoint the log sink can be marked `piiSafe` only as long as data is appropriately tagged in messages and/or the names of custom properties that might have PII values in additional data are pre-configured AND the endpoint implementer has verified and ensured their receiving code will remove marked PII data immediately before sending logs to any output or storage media.\n\nTo tag any part of the message parameter in a log statement as containing PII within the actual log message use the `tagPII` utility. The `tagPII` utility will indicate to a remote backend that the text between the tags should be stripped or masked before sending logs to any output or storage media.\n\nExample tagged output:\n\n```js\nimport { tagPII } from \"@prftesp/cle-logger\";\n\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\n```\n\n## Advanced Configuration\n\n### Remote logging\n\nThe library provides a built in way to transmit logs to a remote http endpoint using the `remoteLogger` logs processor. The `remoteLogger` can be directed at a known listener such as CLE or you can create your own custom log listener. In addition, the log level in effect can be controlled remotely setting the `minLogLevelOverrideUrl` to an http address that will return the current log level to use instead of any defined default logLevel on each `logSink`. The `minLogLevelOverrideUrl` can point to a known central API such as CLE or you can create your own endpoint.\n\nThe following sections outline how to create your own endpoints for either receiving log messages from the `remoteLogger` log sink or providing the current log level override to it.\n\n**Remote log endpoint**\n\nTo implement your own remote logging endpoint, all you have to do is accept `POST` requests, with a JSON payload of an array of [log objects](#log-object), and return either `{ success: true }` or `{ success: false }`. The endpoint should always return a `200 OK` status even if your server code fails so that the CLE logger does not cause failures in the business logic in which it is embedded. A good use-case would be leveraging a serverless api platform to output logs to a queue.\n\nExample of an Azure Function that can receive requests from the `remoteLogger` log sink:\n\n```js\nmodule.exports = async function(context, req) {\n\tcontext.log(req.body); // body will have the log array\n\n\tcontext.res = {\n\t\tstatus: 200,\n\t\tbody: { success: true },\n\t};\n};\n```\n\n**Remote log level override**\n\nThe `minLogLevelOverrideUrl` configuration property is used to provide an endpoint that accepts `GET` requests and a `accountSid` query string parameter. `accountSid` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the `minLogLevel` configuration property. If null is returned, the `minLogLevel` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a `200 OK` status code and use the `success` property to indicate failure.\n\nExample success response:\n\n```json\n{\n\t\"success\": true,\n\t\"minLogLevel\": \"warn\"\n}\n```\n\nExample error response:\n\n```json\n{\n\t\"success\": false,\n\t\"minLogLevel\": null\n}\n```\n\n## Changelog\n\n### v2.0.7\n\n**New features**\n\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the call sid during a call, and automatically include it in `customProperties` log property for all logs that happen during that call (including Flex Action logs).\n\n### v2.0.5\n\n**New features**\n\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the worker sid and automatically include it in `customProperties` log property.\n\n**Other updates**\n\n- Updated documentation based on user feedback.\n\n### v2.0.4\n\n**Bug fixes**\n\n- Fixed issue with logging to console in Twilio Functions. The `consoleLogger` will now output the log object as serialized json.\n\n### v2.0.0 [2019-12-20]\n\n**New features**\n\n- All logging output is now configured using the `logSinks` property. The library provides two commonly used loggers - `consoleLogger` and a `remoteLogger` that accepts the log url as a parameter. [Configuration](#configuration)\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\n- Log functions have a new optional `containsPII` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\n- A new helper function `tagPII` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\n\n**Breaking changes**\n\n- `customLogSink` configuration renamed to `logSinks`\n- Removed `disableRemoteLogging` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\n- Removed `logToConsole` configuration. This is now configured as a log sink with a `consoleLogger` helper function.\n- Removed `logUrl` configuration. This is now configured as a log sink with a `remoteLogger` helper function.\n- Removed `minLogLevel` configuration. This is now configured per log sink.\n\n**Migration guide**\n\n```js\nimport cleLogger from \"@prftesp/cle-logger\";\n\n// v1.0.0 and older config\ncleLogger.init({\n\tdisableRemoteLogSinks: true, // no direct replacement\n\tlogToConsole: true, // replaced by the \"console logger\" sink\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\n\tcustomLogSinks: [\n\t\t{\n\t\t\tname: \"custom sink\",\n\t\t\tlog: logs => {\n\t\t\t\t/* your custom logging */\n\t\t\t},\n\t\t},\n\t],\n});\n\n// v2.0.0 config\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"custom sink\",\n\t\t\tlog: logs => {\n\t\t\t\t/* your custom logging */\n\t\t\t},\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"console logger\",\n\t\t\tlog: consoleLogger,\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"remote logger\",\n\t\t\tlog: remoteLogger(\"http://log.output/api\"),\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t],\n});\n```\n\n### v1.0.0 [2019-09-12]\n\n- Initial release\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.0.7-beta.20200221.4","_nodeVersion":"12.16.0","_npmVersion":"6.13.7","dist":{"integrity":"sha512-rj/5Pp6A8136IMJNnSMoAhpffW8v+W5PrTiGFtcOIZM6n1dxnEfv8yaMTLaskD4I18ME40yABXnI0uxtINcmTg==","shasum":"35e149a5334cbe85d641dd3d25ceda2812f763d4","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.0.7-beta.20200221.4.tgz","fileCount":17,"unpackedSize":77124,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeT/RgCRA9TVsSAnZWagAAYa8QAIcau4s4onP9NFz78bHO\n8sIuncZ6iCqOLA3JKo4LAGYM8IAdKFmJr87lUPdThcNhAe3ATmiSilI+N1RU\n8mplSygO3rEVZtGoU352EDBqQLBdOt1n6QwsDpqCexmcVCfKeBGClD+2qwBx\njkakR+1hZBsAR5PPxYqMKndJ4Maht2cu8ucN7Fnfr7yWQ3MkhDd6Peb4c+Zi\nBxFe/Qo5MQFpV3mOKFzM/HYpemrDfLhl8d+rM9SxisRqHtNv/jXjWh4UTB2Y\nY+NPeT4CaqREDlUORXwbA/sEgD/RsUnTQEZLrl9WFbeOrInMN+TzDBrXdz0W\nFVga/xZ712zTCd+iG5httWKYbuZPvwNsvRzT92VvAkgsXCVKIS8fbQPRWN9Z\n8+OGBXd/ZqXaoDb8/a49dkvLycka6tIWitcVjO9gx8xyyOtgYpDaqxsdUtT6\nfHfDgyZXGFPWSbJHSxlF9GkX/uy9LQXT77YhCi74YPwgqNDLz3X14tlJxztw\nff7eJdH2Br9TMgi2ry+LWBcAfLphcV1biceHuqlzcZknEgirOa44yPcKVvFz\n9qHgBsP9uTSLMA6mqblQjpMYSCLUbuTl+LNe6SkSR2p9wbY/0xxELX3rcVa9\nL+4vDNYRfAjNoj/tqUczMu/kMpdBxemktiLx6vc0hEVmVHDrGWx00UIi+Tx+\nOS9G\r\n=r0oJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCN0YFUobIYPefjFN3jeTX9bo0d8WtRV+MnR0BN634EjAIgA0StMZrAH3AUD+DT8G7SQo9kmpMhx2cj6t2ll8PDuBE="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.0.7-beta.20200221.4_1582298208512_0.37731493346752165"},"_hasShrinkwrap":false},"2.0.7":{"name":"@prftesp/cle-logger","version":"2.0.7","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"_id":"@prftesp/cle-logger@2.0.7","_nodeVersion":"12.16.0","_npmVersion":"6.13.7","dist":{"integrity":"sha512-+w/Q0jOmPiuvrEj9hZ6c6kUIaCHLaL5iNlqKm73OhyiIPBMcmHyL0oM1t2rqgus7rRg/I2Om/D0D5vicHuvykQ==","shasum":"b46356179014e67f5f9a5ac819b6b263f807a5fe","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.0.7.tgz","fileCount":17,"unpackedSize":77025,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeT/TdCRA9TVsSAnZWagAA1gUP/3Jb9CGxykQG5VSiNGrh\nMwNH0fvqcz6ei+eHiKwagkaXJXrdqc2IsesdVm2XBhgOJBdXpej6gq2u93de\nDieUhUOrF0Bk+pw+6P3JcY9DHH0vEuY9/HK3LRBRfXGRt3HVjHa9UdxZSwAW\nPa8VL+zF9vj7nwBmkmKKlEaPLaCMFxygkkgONRoATxfZAQ9Jdkij+vM9A/Lh\nor/pQ5uEnMwUUPx1ppjIpxqVCNGtYVjSuhItvTXnJ0ikWe8CgKEqHdlfbYAV\niC06s014U9s5JKiqrseQOwUL9OabP10BTFYILmAf15WVfnhpVNM55MIINIKY\nAko4I1IOHq/YDJRMxCgtHD2BktvLSFMXo2VKB4IVp/SYmXZ3ZEOz8g1cPAHu\nClo+PsI8wQ0kqhR4lpomlYCNsx38IZ8ekNXeu0oBqKhCkaA91ojgBOBeipE3\ny05AphaHWzyz5qQF0JjRZwM5vH36msm6QEtvAVa3UNNoO0I9/uKfQlKCxn0s\nTopqAQTOHnTCqjwGjjdxurZVUvXgWaRYOg3rzcX75sCswEghOTYQHjKPQdo9\nc6Rx6FPTQF6NijyVZmTnf1q0pPqt1JzZYXnZaUo4Mp0rkn658b6o/wht4axT\n/KyFBtkwhUcLTobppruFFOLaSqJb2O7li32AlTS31L2jaDV5+2F56Kwzl4IH\nfepa\r\n=g+Ju\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCqU2+dInJlhKiZ3XB+Ma7JkjXWc1laoPFgQf7lMkrHJQIgFb7pN1CsY6RuggReGS35eRZYryJ6W7BH2mFTSalPYuw="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.0.7_1582298333243_0.5878857970358233"},"_hasShrinkwrap":false},"2.1.7-beta-es5":{"name":"@prftesp/cle-logger","version":"2.1.7-beta-es5","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿# CLE Logger\r\n\r\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\r\n\r\n## Documentation\r\n\r\n- [Target Environment](#target-environment)\r\n- [Simple Example](#simple-example)\r\n- [Installation](#installation)\r\n- [Features](#features)\r\n- [Configuration](#configuration)\r\n- [API](#api)\r\n  - [Log sinks](#log-sinks)\r\n  - [Logging statements](#logging-statements)\r\n  - [Logging parameters](#logging-parameters)\r\n  - [Logging constants](#logging-constants)\r\n  - [Log codes](#log-codes)\r\n  - [Registering Twilio Actions](#registering-twilio-actions)\r\n  - [Twilio Event Hooks](#twilio-event-hooks)\r\n  - [Version](#version)\r\n- [Log Object](#log-object)\r\n- [Handling PII Data](#handling-pii-data)\r\n- [Advanced Configuration](#advanced-configuration)\r\n  - [Remote logging](#remote-logging)\r\n- [Changelog](#changelog)\r\n\r\n## Target Environment\r\n\r\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\r\n\r\n## Simple Example\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\tname: \"console sink\",\r\n\t\t\tlog: cleLogger.consoleLogger,\r\n\t\t\tpiiSafe: true,\r\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\r\n\t\t},\r\n\t],\r\n});\r\n\r\n// will log message to console\r\ncleLogger.info(\"log message\");\r\n```\r\n\r\n## Installation\r\n\r\n```bash\r\n$ npm install @prftesp/cle-logger\r\n```\r\n\r\n## Features\r\n\r\n- Runs in Node.js and the browser\r\n- Remote logging\r\n- Multiple log sinks\r\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\r\n- Remote minimum log level overrides\r\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\r\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\r\n- Support for tagging logs with custom component names or correlation tokens\r\n- Send Twilio event hook data to a custom backend\r\n\r\n## Configuration\r\n\r\nTo configure the logger call the `init` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\r\n\r\n**Available options**\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// Twilio account sid, required for use with CLE\r\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\r\n\r\n\t// Will be associated with all logs,\r\n\t// if not provided, a unique uuid will be generated\r\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\r\n\t// An array of that will receive an array of log objects,\r\n\t// and can optionally return a promise. Please see the\r\n\t// \"Log Sinks\" section for more information.\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\t// name of the log sink\r\n\t\t\tname: \"exampleConsoleSink\",\r\n\t\t\t// log handler function\r\n\t\t\tlog: cleLogger.consoleLogger,\r\n\t\t\t// This flag defines if the log message is processed by this sink when the contains\r\n\t\t\t// PII parameter of the log statement is true. piiSafe of true indicates all log\r\n\t\t\t// messages are allowed (even when contains PII is true); piiSafe of false indicates\r\n\t\t\t// only log messages with contains PII set to false will be processed by this log sink.\r\n\t\t\t// Defaults to false\r\n\t\t\tpiiSafe: true,\r\n\t\t\t// log level for the sink, defaults to INFO\r\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\r\n\t\t},\r\n\t],\r\n\r\n\t// A way to suppress error messages if set to false, defaults to true\r\n\tlogErrorsToConsole: true,\r\n\r\n\t// If logging Twilio Flex Actions, provide a list of actions\r\n\t// to be excluded from logs\r\n\texcludedFlexActionNames: [\"SendTyping\"],\r\n\r\n\t// A custom endpoint to fetch log level overrides\r\n\t// NOTE: the provided url should always end with 'accountSid=',\r\n\t// the library will append the accountSid\r\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\r\n\r\n\t// An instance of ('@twilio/flex-ui').Manager.\r\n\t// If provided, it will be used to get the worker sid and call sid (only during a call), and \r\n\t// automatically include it in 'customProperties' log property and with every log\r\n\ttwilioFlexManager: manager,\r\n\r\n\t// To send data from Twilio event hooks, to a custom backend,\r\n\t// configure one or more endpoints for each event type\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\r\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\r\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\r\n\t},\r\n});\r\n```\r\n\r\n## API\r\n\r\nThe logger can be imported as an object in CommonJs or ES6 syntax:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n// or\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n```\r\n\r\nAlternatively, all top level members of the `cleLogger` are available as named exports and can be imported separately:\r\n\r\n```js\r\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tlogSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\r\n\r\n// vs\r\n\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tcleLogger.logSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\r\n```\r\n\r\n### Log sinks\r\n\r\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the `logSinks` config. The logSinks configuration defines an array of objects with each object defining the log sink activity. All provided log sinks will be executed in parallel. A log sink configuration must include the following properties:\r\n\r\n- `name`: this is an identifiable name for the logger and must be unique per log sink defined\r\n- `log`: this indicates the logic to process on each batch of logs processed by the sink. There are several built in processing functions that can be used or you can insert any code inline with this definition. The built in processing functions are `consoleLogger` which will send the log statements to the attached console or `remoteLogger` which will send the log statements to the specified url provided as a definition on the parameter (i.e. `remoteLogger(\"https://mylogger.url\"`). For a custom log processing implementation, the logic must be async operations and must return a Promise object. The custom log method will receive an array of log objects for you to handle. You can find out more about the log object type in the [Log Object](#log-object) section. Here is an example of a custom log function:\r\n\r\n```js\r\nlogs => {\r\n\t// log each received log to the console separately\r\n\tlogs.forEach(log => console.log(log));\r\n\treturn Promise.resolve();\r\n};\r\n```\r\n\r\n- `logLevel`: defines the minimum default log level that will be processed by this log sink. Log levels lower than the minimum level will not be processed by this log sink. The minimum default log level can be overridden at run time by the value returned from the overall `minLogLevelOverrideUrl` that is set up in the library `init`.\r\n- `piiSafe`: indicates if log messages with a `containsPII` parameter value of true should be processed by the log sink. If `containsPII` parameter value on the log message is true and piiSafe is false then the log message will not be processed by this log sink, if piiSafe is true then it will. If the `containsPII` parameter value is false on the log message than it is always processed by the log sink regardless of the piiSafe value.\r\n\r\nCustom log sink configuration example. In this example, the custom code executed for each log statement is to append the log into a file. Note how a Promise is returned as required:\r\n\r\n```js\r\n{\r\n\t// name of the log sink\r\n\tname: \"exampleConsoleSink\",\r\n\t// log handler function - this is where you put your custom handling code and\r\n\t// remember to always return a promise. If your operation is not asynchronous,\r\n\t// you can do this by returning Promise.resolve().\r\n\tlog: logs => {\r\n\t\tfs.appendFile(\r\n\t\t\t\"./log.txt\",\r\n\t\t\t`${JSON.stringify(logs)}`,\r\n\t\t\tconsole.log\r\n\t\t);\r\n\t\treturn Promise.resolve();\r\n\t},\r\n\t// should this log sink accept PII data, defaults to false\r\n\tpiiSafe: true,\r\n\t// log level for the sink, defaults to INFO\r\n\tlogLevel: cleLogger.logLevels.VERBOSE\r\n}\r\n```\r\n\r\nExample log sink configuration that uses a custom coded sink and both standard console and remote logging sinks:\r\n\r\n```js\r\nconst fs = require(\"fs\");\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\ncleLogger.init({\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\tname: \"fileSink\",\r\n\t\t\tlog: logs => {\r\n\t\t\t\tfs.appendFile(\"./log.txt\", `${JSON.stringify(logs)}`, console.log);\r\n\t\t\t\treturn Promise.resolve();\r\n\t\t\t},\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"remoteSink\",\r\n\t\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\r\n\t\t\tlogLevel: cleLogger.logLevels.WARN,\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"consoleSink\",\r\n\t\t\tlog: cleLogger.consoleLogger,\r\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\r\n\t\t},\r\n\t],\r\n});\r\n```\r\n\r\n### Logging statements\r\n\r\nTo create logs, you can use one of the logging function available. They all take the same arguments, described below, and log at a corresponding log level. For example, if you use the `cleLogger.verbose` logging function, and your log sink is configured with `logLevel: cleLogger.logLevels.VERBOSE`, that log sink will receive the log. If your log sink is configured with a higher log level, for example `logLevel: cleLogger.logLevels.INFO`, then it would not receive the verbose log. \r\n\r\nHere are the available logging functions:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(...);\r\ncleLogger.info(...);\r\ncleLogger.warn(...);\r\ncleLogger.error(...);\r\ncleLogger.critical(...);\r\n```\r\n\r\nThe functions are all async, return `Promise<void>`, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\r\n\r\n### Logging parameters\r\n\r\nAll logging functions take the same parameters:\r\n\r\n- `message`: the only required property, no logs will be generated without it\r\n  - type: string\r\n  - required: yes\r\n- `additionalData`: any additional log data you would like to include with the log, can be any data type\r\n  - type: any\r\n  - required: no\r\n  - default value: null\r\n- `customProperties`: a single level deep object of important properties you would like to distinguish from general log data, like various sids CLE uses it to index these properties in Application Insights. For CLE, try to use keys found in `cleLogger.customPropertyKeys`\r\n  - type: object\r\n  - required: no\r\n  - default value: {}\r\n- `componentName`: another useful way to 'tag' your logs for easier searching. Can be any string, but for CLE, try to use available options in `cleLogger.componentNames`\r\n  - type: string\r\n  - required: no\r\n  - default value: \"\"\r\n- `logCode`: log code, can be any number used to categorize logs. For defined ranges see the [log codes](#log-codes) section.\r\n  - type: number\r\n  - required: no\r\n  - default value: 0\r\n- `correlationToken`: correlation token to be associated with this log only\r\n  - type: string\r\n  - required: no\r\n  - default value: token provided in `init`, or a generated token if one wasn't provided\r\n- `containsPII`: does the log contain PII data, defaults to false. If set to true, the log will be passed in only to those log sinks that have `piiSafe` set to true. For more info see the [handling PII data](#handling-pii-data) section.\r\n  - type: boolean\r\n  - required: no\r\n  - default value: false\r\n\r\nExample verbose logging statement:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(\r\n\t\"Log message\", // message\r\n\t{ data: \"test\" }, // additionalData\r\n\t{ [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" }, // customProperties\r\n\tcleLogger.componentNames.SESSION, // componentName\r\n\t70123, // logCode\r\n\t\"my-token\", // correlationToken\r\n\ttrue, // containsPII\r\n);\r\n```\r\n\r\n### Logging constants\r\n\r\n**Logging levels**\r\n\r\nLogging level constants should be used when configuring the log level of a [log sink](#log-sinks).\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.logLevels.VERBOSE; // verbose\r\ncleLogger.logLevels.INFO; // info\r\ncleLogger.logLevels.WARN; // warn\r\ncleLogger.logLevels.ERROR; // error\r\ncleLogger.logLevels.CRITICAL; // critical\r\n```\r\n\r\n**Component names**\r\n\r\nDefines a set of commonly used components that should be used for the `componentName` parameter value of a log statement if you are logging from one of these common components in a Twilio implementation.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.componentNames.SESSION;\r\ncleLogger.componentNames.DIALER;\r\ncleLogger.componentNames.DASHBOARD;\r\ncleLogger.componentNames.AFTERCALL;\r\ncleLogger.componentNames.SUPERVISOR;\r\n```\r\n\r\n**Custom property keys**\r\n\r\nDefines a set of commonly used key that should be used when defining the custom properties object that is supplied in the `customProperties` parameter of any logging statement.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.customPropertyKeys.CHANNEL_SID;\r\ncleLogger.customPropertyKeys.TASK_SID;\r\ncleLogger.customPropertyKeys.CALL_SID;\r\ncleLogger.customPropertyKeys.WORKFLOW_SID;\r\ncleLogger.customPropertyKeys.WORKER_SID;\r\n```\r\n\r\n### Log codes\r\n\r\nIt can be helpful when investigating issues to only view logs related to specific areas of a system. Use Log Codes to give a category definition for what area of the contact center the log activity occurred within. Use numbers within the following pre-defined log code ranges or define custom log codes by using your own numbers higher than 300,000.\r\n\r\n- 100 000 - 109 999 => CALL_STATUS\r\n- 110 000 - 119 999 => IVR\r\n- 120 000 - 129 999 => ROUTING\r\n- 130 000 - 139 999 => AGENT_ACTION\r\n- 140 000 - 149 999 => TRANSFER\r\n- 150 000 - 159 999 => RM_INTEGRATION\r\n- 160 000 - 169 999 => WFM_INTEGRATION\r\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\r\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\r\n\r\n### Registering Twilio Actions\r\n\r\nIt is important to log information from the Twilio Flex UI for all implementations even when there is little customization happening. Many developers find it useful to generate log activity when some, or any, of the Twilio Actions occur within the Twilio Flex UI. Twilio Actions are events that arise from the Twilio Flex UI base implementation and indicate when certain activity is occurring - such as an agent hanging up a call. The CLE Logger library can be setup to automatically register all, or selected, Twilio Actions such that right after any code tied to the Action event has been executed a log statement is written indicating the Action occurred. This log statement will also automatically include the action event payload. To enable this logging capability, use the following approach to register the Twilio Actions. This only needs to be performed once during the initialization of the Twilio Flex UI page on the client.\r\n\r\nExample in a Twilio Flex UI plugin:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\n/**\r\n * @param flex { typeof import('@twilio/flex-ui') }\r\n * @param manager { import('@twilio/flex-ui').Manager }\r\n */\r\ninit(flex, manager) {\r\n\tcleLogger.init({\r\n\t\taccountSid: \"your-account-sid\"\r\n\t});\r\n\r\n\tcleLogger.registerTwilioActions(flex);\r\n}\r\n```\r\n\r\n**Note**: A Twilio account sid must be provided during `init` with the `accountSid` parameter, otherwise the actions won't be logged.\r\n\r\nThe logger will loop through all actions found under `flex.Actions.actions`, and attach `after` event handlers to log the action name and payload. Any private properties found in the payload (starting with '\\_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Not all Action events need to be logged and by using the `excludedFlexActionNames` property in the logger configuration the Action events not desired can be ignored. Any actions names configured in `excludedFlexActionNames` will not be included in the logs.\r\n\r\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\r\n\r\n**Twilio Actions log levels**\r\n\r\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of `INFO`.\r\n\r\n```js\r\nAcceptTask: INFO;\r\nCancelTransfer: INFO;\r\nCompleteTask: INFO;\r\nHangupCall: INFO;\r\nHideDirectory: VERBOSE;\r\nHistoryGo: VERBOSE;\r\nHistoryGoBack: VERBOSE;\r\nHistoryGoForward: VERBOSE;\r\nHistoryPush: VERBOSE;\r\nHistoryReplace: VERBOSE;\r\nHoldCall: INFO;\r\nHoldParticipant: INFO;\r\nKickParticipant: INFO;\r\nLogout: INFO;\r\nMonitorCall: INFO;\r\nNavigateToView: INFO;\r\nRejectTask: INFO;\r\nSelectTask: INFO;\r\nSelectTaskInSupervisor: INFO;\r\nSelectWorkerInSupervisor: INFO;\r\nSendMessage: INFO;\r\nSendTyping: VERBOSE;\r\nSetActivity: INFO;\r\nSetInputText: VERBOSE;\r\nShowDirectory: VERBOSE;\r\nStopMonitoringCall: INFO;\r\nToggleMute: INFO;\r\nToggleSidebar: VERBOSE;\r\nTransferTask: INFO;\r\nUnholdCall: INFO;\r\nUnholdParticipant: INFO;\r\nWrapupTask: INFO;\r\n```\r\n\r\n### Twilio Event Hooks\r\n\r\nCurrently, the Twilio console only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with `twilioEventHooks` option, and use the following functions to send event data to a custom endpoint.\r\n\r\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is `application/x-www-form-urlencoded` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// unrelated init properties omitted from this example.\r\n\t// include in the twilioEventHooks array an entry for only the event hooks\r\n\t// the executing code is going to relay. For example, if the code is only\r\n\t// handling task router web hook events then you would only need to define\r\n\t// the taskRouter relay url here.\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\r\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\r\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\r\n\t},\r\n});\r\n\r\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\r\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\r\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\r\n```\r\n\r\nExample Azure Function hook for debugger events:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\nmodule.exports = async function(context, req) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\r\n\t\t},\r\n\t});\r\n\r\n\t// log to console\r\n\tcleLogger.info(\"Received Twilio debugger event\");\r\n\r\n\t// send event data to a configured url\r\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\r\n};\r\n```\r\n\r\n**NOTE WHEN USING THIS FUNCTIONALITY IN TWILIO FUNCTIONS:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to `x-www-form-urlencoded` format.\r\n\r\nExample Twilio function task router relay using the `form-urlencoded` npm package to convert the data to `x-www-form-urlencoded` format:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\nconst formurlencoded = require(\"form-urlencoded\").default;\r\n\r\nexports.handler = async function(context, event, callback) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\r\n\t\t},\r\n\t});\r\n\r\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\r\n\r\n\tcallback();\r\n};\r\n```\r\n\r\n### Version\r\n\r\nVersion will be automatically included in all logs.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.version; // 1.0.0\r\n```\r\n\r\n## Log Object\r\n\r\nThe following is the type definition of the log object received by [log sinks](#log-sinks). It consists of properties provided in log messages, as well as some that are automatically inserted like the `eventDate`. You can use this information to customize your logs inside a custom log sink (e.g. filter out unwanted logs, modify some of the properties to include more data, or any kind of conditional logic based on the value of these properties). If using the built in `remoteLogger` log processor with your custom backend, you can expect an array of these objects to be sent to your log endpoint. For more info about implementing a custom logging endpoint see the [remote logging](#remote-logging) section.\r\n\r\n```js\r\n{\r\n\t// Account sid if provided during init\r\n\taccountSid: string;\r\n\r\n\t// Any additional log data to be included with the log\r\n\tadditionalData: unknown;\r\n\r\n\t// Component name, if provided\r\n\tcomponentName: string;\r\n\r\n\t// Generated or configured token\r\n\tcorrelationToken: string;\r\n\r\n\t// Current UTC datetime in ISO 8601 format\r\n\teventDate: string;\r\n\r\n\t// Log level\r\n\tlevel: string;\r\n\r\n\t// Log code, if provided\r\n\tlogCode: number;\r\n\r\n\t// Log message\r\n\tmessage: string;\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\tproperties: object;\r\n\r\n\t// BrowserFunction if the environment is the browser\r\n\t// CloudFunction if the environment is Node.js\r\n\tsource: string;\r\n\r\n\t// Indicates that the log contains PII data\r\n\tcontainsPII: boolean;\r\n\r\n\t// Logging library version\r\n\tloggerVersion: string;\r\n}\r\n```\r\n\r\n## Handling PII Data\r\n\r\nThe logger provides support for helping to protect log messages or data that need to include PII (Personally Identifiable Information). While the logger has several mechanisms that can be used to flag logs that contain PII and block PII from going to particular log sinks, it is still ultimately the developers responsibility to ensure PII is flagged or marked appropriately for the logger to know when it exists - the logger cannot automatically detect if PII is part of a log statement or data object. The general guideline is that developers should avoid including any PII data in log information whenever possible. In the instances where PII data must be included there are several mechanisms the logger supports to help indicate PII data and protect where it is sent.\r\n\r\nA `containsPII` parameter exists on log statement methods. This flag should be set to true if there is PII data, or any potential chance of it, in the log message, additional data or custom properties data. When this flag is set to true then it will only allow log sinks that have the `piiSafe` configuration set to true to receive and process the log message. `piiSafe` indicates on a log sink whether that log sink receiver is able to internally store PII data OR has logic to strip PII data that is tagged in the message or from pre-configured additional data fields before the log is written to any storage or output device. When sending data to any `remoteLogger` endpoint the log sink can be marked `piiSafe` only as long as data is appropriately tagged in messages and/or the names of custom properties that might have PII values in additional data are pre-configured AND the endpoint implementer has verified and ensured their receiving code will remove marked PII data immediately before sending logs to any output or storage media.\r\n\r\nTo tag any part of the message parameter in a log statement as containing PII within the actual log message use the `tagPII` utility. The `tagPII` utility will indicate to a remote backend that the text between the tags should be stripped or masked before sending logs to any output or storage media.\r\n\r\nExample tagged output:\r\n\r\n```js\r\nimport { tagPII } from \"@prftesp/cle-logger\";\r\n\r\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\r\n```\r\n\r\n## Advanced Configuration\r\n\r\n### Remote logging\r\n\r\nThe library provides a built in way to transmit logs to a remote http endpoint using the `remoteLogger` logs processor. The `remoteLogger` can be directed at a known listener such as CLE or you can create your own custom log listener. In addition, the log level in effect can be controlled remotely setting the `minLogLevelOverrideUrl` to an http address that will return the current log level to use instead of any defined default logLevel on each `logSink`. The `minLogLevelOverrideUrl` can point to a known central API such as CLE or you can create your own endpoint.\r\n\r\nThe following sections outline how to create your own endpoints for either receiving log messages from the `remoteLogger` log sink or providing the current log level override to it.\r\n\r\n**Remote log endpoint**\r\n\r\nTo implement your own remote logging endpoint, all you have to do is accept `POST` requests, with a JSON payload of an array of [log objects](#log-object), and return either `{ success: true }` or `{ success: false }`. The endpoint should always return a `200 OK` status even if your server code fails so that the CLE logger does not cause failures in the business logic in which it is embedded. A good use-case would be leveraging a serverless api platform to output logs to a queue.\r\n\r\nExample of an Azure Function that can receive requests from the `remoteLogger` log sink:\r\n\r\n```js\r\nmodule.exports = async function(context, req) {\r\n\tcontext.log(req.body); // body will have the log array\r\n\r\n\tcontext.res = {\r\n\t\tstatus: 200,\r\n\t\tbody: { success: true },\r\n\t};\r\n};\r\n```\r\n\r\n**Remote log level override**\r\n\r\nThe `minLogLevelOverrideUrl` configuration property is used to provide an endpoint that accepts `GET` requests and a `accountSid` query string parameter. `accountSid` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the `minLogLevel` configuration property. If null is returned, the `minLogLevel` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a `200 OK` status code and use the `success` property to indicate failure.\r\n\r\nExample success response:\r\n\r\n```json\r\n{\r\n\t\"success\": true,\r\n\t\"minLogLevel\": \"warn\"\r\n}\r\n```\r\n\r\nExample error response:\r\n\r\n```json\r\n{\r\n\t\"success\": false,\r\n\t\"minLogLevel\": null\r\n}\r\n```\r\n\r\n## Changelog\r\n\r\n### v2.0.7\r\n\r\n**New features**\r\n\r\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the call sid during a call, and automatically include it in `customProperties` log property for all logs that happen during that call (including Flex Action logs).\r\n\r\n### v2.0.5\r\n\r\n**New features**\r\n\r\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the worker sid and automatically include it in `customProperties` log property.\r\n\r\n**Other updates**\r\n\r\n- Updated documentation based on user feedback.\r\n\r\n### v2.0.4\r\n\r\n**Bug fixes**\r\n\r\n- Fixed issue with logging to console in Twilio Functions. The `consoleLogger` will now output the log object as serialized json.\r\n\r\n### v2.0.0 [2019-12-20]\r\n\r\n**New features**\r\n\r\n- All logging output is now configured using the `logSinks` property. The library provides two commonly used loggers - `consoleLogger` and a `remoteLogger` that accepts the log url as a parameter. [Configuration](#configuration)\r\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\r\n- Log functions have a new optional `containsPII` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\r\n- A new helper function `tagPII` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\r\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\r\n\r\n**Breaking changes**\r\n\r\n- `customLogSink` configuration renamed to `logSinks`\r\n- Removed `disableRemoteLogging` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\r\n- Removed `logToConsole` configuration. This is now configured as a log sink with a `consoleLogger` helper function.\r\n- Removed `logUrl` configuration. This is now configured as a log sink with a `remoteLogger` helper function.\r\n- Removed `minLogLevel` configuration. This is now configured per log sink.\r\n\r\n**Migration guide**\r\n\r\n```js\r\nimport cleLogger from \"@prftesp/cle-logger\";\r\n\r\n// v1.0.0 and older config\r\ncleLogger.init({\r\n\tdisableRemoteLogSinks: true, // no direct replacement\r\n\tlogToConsole: true, // replaced by the \"console logger\" sink\r\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\r\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\r\n\tcustomLogSinks: [\r\n\t\t{\r\n\t\t\tname: \"custom sink\",\r\n\t\t\tlog: logs => {\r\n\t\t\t\t/* your custom logging */\r\n\t\t\t},\r\n\t\t},\r\n\t],\r\n});\r\n\r\n// v2.0.0 config\r\ncleLogger.init({\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\tname: \"custom sink\",\r\n\t\t\tlog: logs => {\r\n\t\t\t\t/* your custom logging */\r\n\t\t\t},\r\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t\t// set to true to get feature parity with older version,\r\n\t\t\t// please consider your use case before setting\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"console logger\",\r\n\t\t\tlog: consoleLogger,\r\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t\t// set to true to get feature parity with older version,\r\n\t\t\t// please consider your use case before setting\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"remote logger\",\r\n\t\t\tlog: remoteLogger(\"http://log.output/api\"),\r\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t\t// set to true to get feature parity with older version,\r\n\t\t\t// please consider your use case before setting\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t],\r\n});\r\n```\r\n\r\n### v1.0.0 [2019-09-12]\r\n\r\n- Initial release\r\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.1.7-beta-es5","_nodeVersion":"10.15.2","_npmVersion":"6.11.3","dist":{"integrity":"sha512-YzpuYYsnDGvTqCraQTliEl3yJubE8zqqfpW9exxMS453vB2m7Egt98NDkVFIkD+zgiXWixHABeloiwxBKVot6w==","shasum":"8c66da32fa91d2b0041f2408ebb1c5692ea3e647","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.1.7-beta-es5.tgz","fileCount":17,"unpackedSize":86891,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeZoARCRA9TVsSAnZWagAAQfoQAKMoDD7fQVJf/PQTiOwm\nd0SSRpS1lbG9DhuwebOTKMW5REVxDIl0sKmvDLd1Y147LW1zkh4jtxHm2iQ6\nHbRCGAhoDzsReFBBM2yyjbI3ZyBduHW5E5oq3wT+MvGN8YhIz2rqwI03Ca2L\nMhLZnjXenQ+SrNxBD3JaYl/iJMJT8d2wBWGd8qbXaO15B1FQaGjLKYNPAFlC\nMLvzb7lWv4c8S1zxPYf57/CQJnyTXx0UdGlxuDWIKJAsVtiC0ctifbVnk3pp\nMjG9DoU0Nujdnppy/TeBUCcskUCbBsesqzYa0MTBKgBr698q2zZ1/tykuCbC\nfrWW5epKVQF8OKtQczT9UyEwDGoDsdrCZ04fxbhbi5OtCeCfSB/TV6Fn+f8r\nSlFKMh9hIJFO5Eb9FAwd5EIjTJpbC+6v2gIAt8ZvmkXRRB9k0SAP7YT+ODW1\ntntiIhIoBQ6YQhv3mRHyV3CR17zIwqSc+NuyioIWshm32rS92E/d9bc9YTvG\nBkR2ULSqJi53ALSXDa1fkUaF7S8FlOtGei19qAIhpnzKhuaCLfDT+t77ExKO\n+876VhYtqLR1TWuOpWYCWB3B+AqBFIVdcux0uJ83PsjTjJcaGQqFcULmQiE+\nyW596By9SNT8vWJRWpvrsq1RYbBGJkqA1n2jYev1bCg3N/ix/4KWEBJMlRdC\n6rXF\r\n=U2Tm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCaqd8zl1sd6D6mx2k2sdMfgIOtOAzW3l+mHpEmO0vq5AIhAIEylb97BzHJ/WVCOw6yCTSHovAYKZvV45hsx1lZC2Ap"}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.1.7-beta-es5_1583775761572_0.890621888943117"},"_hasShrinkwrap":false},"2.1.7-beta-es5-1":{"name":"@prftesp/cle-logger","version":"2.1.7-beta-es5-1","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^24.0.15","@types/node":"^12.0.10","@typescript-eslint/eslint-plugin":"^1.11.0","@typescript-eslint/parser":"^1.11.0","eslint":"^6.0.1","eslint-config-prettier":"^6.0.0","eslint-plugin-prettier":"^3.1.0","jest":"^24.8.0","prettier":"^1.18.2","rimraf":"^2.6.3","ts-jest":"^24.0.2","typescript":"^3.5.2"},"dependencies":{"axios":"^0.19.0"},"readme":"﻿# CLE Logger\r\n\r\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\r\n\r\n## Documentation\r\n\r\n- [Target Environment](#target-environment)\r\n- [Simple Example](#simple-example)\r\n- [Installation](#installation)\r\n- [Features](#features)\r\n- [Configuration](#configuration)\r\n- [API](#api)\r\n  - [Log sinks](#log-sinks)\r\n  - [Logging statements](#logging-statements)\r\n  - [Logging parameters](#logging-parameters)\r\n  - [Logging constants](#logging-constants)\r\n  - [Log codes](#log-codes)\r\n  - [Registering Twilio Actions](#registering-twilio-actions)\r\n  - [Twilio Event Hooks](#twilio-event-hooks)\r\n  - [Version](#version)\r\n- [Log Object](#log-object)\r\n- [Handling PII Data](#handling-pii-data)\r\n- [Advanced Configuration](#advanced-configuration)\r\n  - [Remote logging](#remote-logging)\r\n- [Changelog](#changelog)\r\n\r\n## Target Environment\r\n\r\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\r\n\r\n## Simple Example\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\tname: \"console sink\",\r\n\t\t\tlog: cleLogger.consoleLogger,\r\n\t\t\tpiiSafe: true,\r\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\r\n\t\t},\r\n\t],\r\n});\r\n\r\n// will log message to console\r\ncleLogger.info(\"log message\");\r\n```\r\n\r\n## Installation\r\n\r\n```bash\r\n$ npm install @prftesp/cle-logger\r\n```\r\n\r\n## Features\r\n\r\n- Runs in Node.js and the browser\r\n- Remote logging\r\n- Multiple log sinks\r\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\r\n- Remote minimum log level overrides\r\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\r\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\r\n- Support for tagging logs with custom component names or correlation tokens\r\n- Send Twilio event hook data to a custom backend\r\n\r\n## Configuration\r\n\r\nTo configure the logger call the `init` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\r\n\r\n**Available options**\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// Twilio account sid, required for use with CLE\r\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\r\n\r\n\t// Will be associated with all logs,\r\n\t// if not provided, a unique uuid will be generated\r\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\r\n\t// An array of that will receive an array of log objects,\r\n\t// and can optionally return a promise. Please see the\r\n\t// \"Log Sinks\" section for more information.\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\t// name of the log sink\r\n\t\t\tname: \"exampleConsoleSink\",\r\n\t\t\t// log handler function\r\n\t\t\tlog: cleLogger.consoleLogger,\r\n\t\t\t// This flag defines if the log message is processed by this sink when the contains\r\n\t\t\t// PII parameter of the log statement is true. piiSafe of true indicates all log\r\n\t\t\t// messages are allowed (even when contains PII is true); piiSafe of false indicates\r\n\t\t\t// only log messages with contains PII set to false will be processed by this log sink.\r\n\t\t\t// Defaults to false\r\n\t\t\tpiiSafe: true,\r\n\t\t\t// log level for the sink, defaults to INFO\r\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\r\n\t\t},\r\n\t],\r\n\r\n\t// A way to suppress error messages if set to false, defaults to true\r\n\tlogErrorsToConsole: true,\r\n\r\n\t// If logging Twilio Flex Actions, provide a list of actions\r\n\t// to be excluded from logs\r\n\texcludedFlexActionNames: [\"SendTyping\"],\r\n\r\n\t// A custom endpoint to fetch log level overrides\r\n\t// NOTE: the provided url should always end with 'accountSid=',\r\n\t// the library will append the accountSid\r\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\r\n\r\n\t// An instance of ('@twilio/flex-ui').Manager.\r\n\t// If provided, it will be used to get the worker sid and call sid (only during a call), and \r\n\t// automatically include it in 'customProperties' log property and with every log\r\n\ttwilioFlexManager: manager,\r\n\r\n\t// To send data from Twilio event hooks, to a custom backend,\r\n\t// configure one or more endpoints for each event type\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\r\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\r\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\r\n\t},\r\n});\r\n```\r\n\r\n## API\r\n\r\nThe logger can be imported as an object in CommonJs or ES6 syntax:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n// or\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n```\r\n\r\nAlternatively, all top level members of the `cleLogger` are available as named exports and can be imported separately:\r\n\r\n```js\r\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tlogSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\r\n\r\n// vs\r\n\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tcleLogger.logSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\r\n```\r\n\r\n### Log sinks\r\n\r\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the `logSinks` config. The logSinks configuration defines an array of objects with each object defining the log sink activity. All provided log sinks will be executed in parallel. A log sink configuration must include the following properties:\r\n\r\n- `name`: this is an identifiable name for the logger and must be unique per log sink defined\r\n- `log`: this indicates the logic to process on each batch of logs processed by the sink. There are several built in processing functions that can be used or you can insert any code inline with this definition. The built in processing functions are `consoleLogger` which will send the log statements to the attached console or `remoteLogger` which will send the log statements to the specified url provided as a definition on the parameter (i.e. `remoteLogger(\"https://mylogger.url\"`). For a custom log processing implementation, the logic must be async operations and must return a Promise object. The custom log method will receive an array of log objects for you to handle. You can find out more about the log object type in the [Log Object](#log-object) section. Here is an example of a custom log function:\r\n\r\n```js\r\nlogs => {\r\n\t// log each received log to the console separately\r\n\tlogs.forEach(log => console.log(log));\r\n\treturn Promise.resolve();\r\n};\r\n```\r\n\r\n- `logLevel`: defines the minimum default log level that will be processed by this log sink. Log levels lower than the minimum level will not be processed by this log sink. The minimum default log level can be overridden at run time by the value returned from the overall `minLogLevelOverrideUrl` that is set up in the library `init`.\r\n- `piiSafe`: indicates if log messages with a `containsPII` parameter value of true should be processed by the log sink. If `containsPII` parameter value on the log message is true and piiSafe is false then the log message will not be processed by this log sink, if piiSafe is true then it will. If the `containsPII` parameter value is false on the log message than it is always processed by the log sink regardless of the piiSafe value.\r\n\r\nCustom log sink configuration example. In this example, the custom code executed for each log statement is to append the log into a file. Note how a Promise is returned as required:\r\n\r\n```js\r\n{\r\n\t// name of the log sink\r\n\tname: \"exampleConsoleSink\",\r\n\t// log handler function - this is where you put your custom handling code and\r\n\t// remember to always return a promise. If your operation is not asynchronous,\r\n\t// you can do this by returning Promise.resolve().\r\n\tlog: logs => {\r\n\t\tfs.appendFile(\r\n\t\t\t\"./log.txt\",\r\n\t\t\t`${JSON.stringify(logs)}`,\r\n\t\t\tconsole.log\r\n\t\t);\r\n\t\treturn Promise.resolve();\r\n\t},\r\n\t// should this log sink accept PII data, defaults to false\r\n\tpiiSafe: true,\r\n\t// log level for the sink, defaults to INFO\r\n\tlogLevel: cleLogger.logLevels.VERBOSE\r\n}\r\n```\r\n\r\nExample log sink configuration that uses a custom coded sink and both standard console and remote logging sinks:\r\n\r\n```js\r\nconst fs = require(\"fs\");\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\ncleLogger.init({\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\tname: \"fileSink\",\r\n\t\t\tlog: logs => {\r\n\t\t\t\tfs.appendFile(\"./log.txt\", `${JSON.stringify(logs)}`, console.log);\r\n\t\t\t\treturn Promise.resolve();\r\n\t\t\t},\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"remoteSink\",\r\n\t\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\r\n\t\t\tlogLevel: cleLogger.logLevels.WARN,\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"consoleSink\",\r\n\t\t\tlog: cleLogger.consoleLogger,\r\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\r\n\t\t},\r\n\t],\r\n});\r\n```\r\n\r\n### Logging statements\r\n\r\nTo create logs, you can use one of the logging function available. They all take the same arguments, described below, and log at a corresponding log level. For example, if you use the `cleLogger.verbose` logging function, and your log sink is configured with `logLevel: cleLogger.logLevels.VERBOSE`, that log sink will receive the log. If your log sink is configured with a higher log level, for example `logLevel: cleLogger.logLevels.INFO`, then it would not receive the verbose log. \r\n\r\nHere are the available logging functions:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(...);\r\ncleLogger.info(...);\r\ncleLogger.warn(...);\r\ncleLogger.error(...);\r\ncleLogger.critical(...);\r\n```\r\n\r\nThe functions are all async, return `Promise<void>`, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\r\n\r\n### Logging parameters\r\n\r\nAll logging functions take the same parameters:\r\n\r\n- `message`: the only required property, no logs will be generated without it\r\n  - type: string\r\n  - required: yes\r\n- `additionalData`: any additional log data you would like to include with the log, can be any data type\r\n  - type: any\r\n  - required: no\r\n  - default value: null\r\n- `customProperties`: a single level deep object of important properties you would like to distinguish from general log data, like various sids CLE uses it to index these properties in Application Insights. For CLE, try to use keys found in `cleLogger.customPropertyKeys`\r\n  - type: object\r\n  - required: no\r\n  - default value: {}\r\n- `componentName`: another useful way to 'tag' your logs for easier searching. Can be any string, but for CLE, try to use available options in `cleLogger.componentNames`\r\n  - type: string\r\n  - required: no\r\n  - default value: \"\"\r\n- `logCode`: log code, can be any number used to categorize logs. For defined ranges see the [log codes](#log-codes) section.\r\n  - type: number\r\n  - required: no\r\n  - default value: 0\r\n- `correlationToken`: correlation token to be associated with this log only\r\n  - type: string\r\n  - required: no\r\n  - default value: token provided in `init`, or a generated token if one wasn't provided\r\n- `containsPII`: does the log contain PII data, defaults to false. If set to true, the log will be passed in only to those log sinks that have `piiSafe` set to true. For more info see the [handling PII data](#handling-pii-data) section.\r\n  - type: boolean\r\n  - required: no\r\n  - default value: false\r\n\r\nExample verbose logging statement:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(\r\n\t\"Log message\", // message\r\n\t{ data: \"test\" }, // additionalData\r\n\t{ [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" }, // customProperties\r\n\tcleLogger.componentNames.SESSION, // componentName\r\n\t70123, // logCode\r\n\t\"my-token\", // correlationToken\r\n\ttrue, // containsPII\r\n);\r\n```\r\n\r\n### Logging constants\r\n\r\n**Logging levels**\r\n\r\nLogging level constants should be used when configuring the log level of a [log sink](#log-sinks).\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.logLevels.VERBOSE; // verbose\r\ncleLogger.logLevels.INFO; // info\r\ncleLogger.logLevels.WARN; // warn\r\ncleLogger.logLevels.ERROR; // error\r\ncleLogger.logLevels.CRITICAL; // critical\r\n```\r\n\r\n**Component names**\r\n\r\nDefines a set of commonly used components that should be used for the `componentName` parameter value of a log statement if you are logging from one of these common components in a Twilio implementation.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.componentNames.SESSION;\r\ncleLogger.componentNames.DIALER;\r\ncleLogger.componentNames.DASHBOARD;\r\ncleLogger.componentNames.AFTERCALL;\r\ncleLogger.componentNames.SUPERVISOR;\r\n```\r\n\r\n**Custom property keys**\r\n\r\nDefines a set of commonly used key that should be used when defining the custom properties object that is supplied in the `customProperties` parameter of any logging statement.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.customPropertyKeys.CHANNEL_SID;\r\ncleLogger.customPropertyKeys.TASK_SID;\r\ncleLogger.customPropertyKeys.CALL_SID;\r\ncleLogger.customPropertyKeys.WORKFLOW_SID;\r\ncleLogger.customPropertyKeys.WORKER_SID;\r\n```\r\n\r\n### Log codes\r\n\r\nIt can be helpful when investigating issues to only view logs related to specific areas of a system. Use Log Codes to give a category definition for what area of the contact center the log activity occurred within. Use numbers within the following pre-defined log code ranges or define custom log codes by using your own numbers higher than 300,000.\r\n\r\n- 100 000 - 109 999 => CALL_STATUS\r\n- 110 000 - 119 999 => IVR\r\n- 120 000 - 129 999 => ROUTING\r\n- 130 000 - 139 999 => AGENT_ACTION\r\n- 140 000 - 149 999 => TRANSFER\r\n- 150 000 - 159 999 => RM_INTEGRATION\r\n- 160 000 - 169 999 => WFM_INTEGRATION\r\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\r\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\r\n\r\n### Registering Twilio Actions\r\n\r\nIt is important to log information from the Twilio Flex UI for all implementations even when there is little customization happening. Many developers find it useful to generate log activity when some, or any, of the Twilio Actions occur within the Twilio Flex UI. Twilio Actions are events that arise from the Twilio Flex UI base implementation and indicate when certain activity is occurring - such as an agent hanging up a call. The CLE Logger library can be setup to automatically register all, or selected, Twilio Actions such that right after any code tied to the Action event has been executed a log statement is written indicating the Action occurred. This log statement will also automatically include the action event payload. To enable this logging capability, use the following approach to register the Twilio Actions. This only needs to be performed once during the initialization of the Twilio Flex UI page on the client.\r\n\r\nExample in a Twilio Flex UI plugin:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\n/**\r\n * @param flex { typeof import('@twilio/flex-ui') }\r\n * @param manager { import('@twilio/flex-ui').Manager }\r\n */\r\ninit(flex, manager) {\r\n\tcleLogger.init({\r\n\t\taccountSid: \"your-account-sid\"\r\n\t});\r\n\r\n\tcleLogger.registerTwilioActions(flex);\r\n}\r\n```\r\n\r\n**Note**: A Twilio account sid must be provided during `init` with the `accountSid` parameter, otherwise the actions won't be logged.\r\n\r\nThe logger will loop through all actions found under `flex.Actions.actions`, and attach `after` event handlers to log the action name and payload. Any private properties found in the payload (starting with '\\_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Not all Action events need to be logged and by using the `excludedFlexActionNames` property in the logger configuration the Action events not desired can be ignored. Any actions names configured in `excludedFlexActionNames` will not be included in the logs.\r\n\r\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\r\n\r\n**Twilio Actions log levels**\r\n\r\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of `INFO`.\r\n\r\n```js\r\nAcceptTask: INFO;\r\nCancelTransfer: INFO;\r\nCompleteTask: INFO;\r\nHangupCall: INFO;\r\nHideDirectory: VERBOSE;\r\nHistoryGo: VERBOSE;\r\nHistoryGoBack: VERBOSE;\r\nHistoryGoForward: VERBOSE;\r\nHistoryPush: VERBOSE;\r\nHistoryReplace: VERBOSE;\r\nHoldCall: INFO;\r\nHoldParticipant: INFO;\r\nKickParticipant: INFO;\r\nLogout: INFO;\r\nMonitorCall: INFO;\r\nNavigateToView: INFO;\r\nRejectTask: INFO;\r\nSelectTask: INFO;\r\nSelectTaskInSupervisor: INFO;\r\nSelectWorkerInSupervisor: INFO;\r\nSendMessage: INFO;\r\nSendTyping: VERBOSE;\r\nSetActivity: INFO;\r\nSetInputText: VERBOSE;\r\nShowDirectory: VERBOSE;\r\nStopMonitoringCall: INFO;\r\nToggleMute: INFO;\r\nToggleSidebar: VERBOSE;\r\nTransferTask: INFO;\r\nUnholdCall: INFO;\r\nUnholdParticipant: INFO;\r\nWrapupTask: INFO;\r\n```\r\n\r\n### Twilio Event Hooks\r\n\r\nCurrently, the Twilio console only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with `twilioEventHooks` option, and use the following functions to send event data to a custom endpoint.\r\n\r\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is `application/x-www-form-urlencoded` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// unrelated init properties omitted from this example.\r\n\t// include in the twilioEventHooks array an entry for only the event hooks\r\n\t// the executing code is going to relay. For example, if the code is only\r\n\t// handling task router web hook events then you would only need to define\r\n\t// the taskRouter relay url here.\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\r\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\r\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\r\n\t},\r\n});\r\n\r\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\r\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\r\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\r\n```\r\n\r\nExample Azure Function hook for debugger events:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\nmodule.exports = async function(context, req) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\r\n\t\t},\r\n\t});\r\n\r\n\t// log to console\r\n\tcleLogger.info(\"Received Twilio debugger event\");\r\n\r\n\t// send event data to a configured url\r\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\r\n};\r\n```\r\n\r\n**NOTE WHEN USING THIS FUNCTIONALITY IN TWILIO FUNCTIONS:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to `x-www-form-urlencoded` format.\r\n\r\nExample Twilio function task router relay using the `form-urlencoded` npm package to convert the data to `x-www-form-urlencoded` format:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\nconst formurlencoded = require(\"form-urlencoded\").default;\r\n\r\nexports.handler = async function(context, event, callback) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\r\n\t\t},\r\n\t});\r\n\r\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\r\n\r\n\tcallback();\r\n};\r\n```\r\n\r\n### Version\r\n\r\nVersion will be automatically included in all logs.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.version; // 1.0.0\r\n```\r\n\r\n## Log Object\r\n\r\nThe following is the type definition of the log object received by [log sinks](#log-sinks). It consists of properties provided in log messages, as well as some that are automatically inserted like the `eventDate`. You can use this information to customize your logs inside a custom log sink (e.g. filter out unwanted logs, modify some of the properties to include more data, or any kind of conditional logic based on the value of these properties). If using the built in `remoteLogger` log processor with your custom backend, you can expect an array of these objects to be sent to your log endpoint. For more info about implementing a custom logging endpoint see the [remote logging](#remote-logging) section.\r\n\r\n```js\r\n{\r\n\t// Account sid if provided during init\r\n\taccountSid: string;\r\n\r\n\t// Any additional log data to be included with the log\r\n\tadditionalData: unknown;\r\n\r\n\t// Component name, if provided\r\n\tcomponentName: string;\r\n\r\n\t// Generated or configured token\r\n\tcorrelationToken: string;\r\n\r\n\t// Current UTC datetime in ISO 8601 format\r\n\teventDate: string;\r\n\r\n\t// Log level\r\n\tlevel: string;\r\n\r\n\t// Log code, if provided\r\n\tlogCode: number;\r\n\r\n\t// Log message\r\n\tmessage: string;\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\tproperties: object;\r\n\r\n\t// BrowserFunction if the environment is the browser\r\n\t// CloudFunction if the environment is Node.js\r\n\tsource: string;\r\n\r\n\t// Indicates that the log contains PII data\r\n\tcontainsPII: boolean;\r\n\r\n\t// Logging library version\r\n\tloggerVersion: string;\r\n}\r\n```\r\n\r\n## Handling PII Data\r\n\r\nThe logger provides support for helping to protect log messages or data that need to include PII (Personally Identifiable Information). While the logger has several mechanisms that can be used to flag logs that contain PII and block PII from going to particular log sinks, it is still ultimately the developers responsibility to ensure PII is flagged or marked appropriately for the logger to know when it exists - the logger cannot automatically detect if PII is part of a log statement or data object. The general guideline is that developers should avoid including any PII data in log information whenever possible. In the instances where PII data must be included there are several mechanisms the logger supports to help indicate PII data and protect where it is sent.\r\n\r\nA `containsPII` parameter exists on log statement methods. This flag should be set to true if there is PII data, or any potential chance of it, in the log message, additional data or custom properties data. When this flag is set to true then it will only allow log sinks that have the `piiSafe` configuration set to true to receive and process the log message. `piiSafe` indicates on a log sink whether that log sink receiver is able to internally store PII data OR has logic to strip PII data that is tagged in the message or from pre-configured additional data fields before the log is written to any storage or output device. When sending data to any `remoteLogger` endpoint the log sink can be marked `piiSafe` only as long as data is appropriately tagged in messages and/or the names of custom properties that might have PII values in additional data are pre-configured AND the endpoint implementer has verified and ensured their receiving code will remove marked PII data immediately before sending logs to any output or storage media.\r\n\r\nTo tag any part of the message parameter in a log statement as containing PII within the actual log message use the `tagPII` utility. The `tagPII` utility will indicate to a remote backend that the text between the tags should be stripped or masked before sending logs to any output or storage media.\r\n\r\nExample tagged output:\r\n\r\n```js\r\nimport { tagPII } from \"@prftesp/cle-logger\";\r\n\r\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\r\n```\r\n\r\n## Advanced Configuration\r\n\r\n### Remote logging\r\n\r\nThe library provides a built in way to transmit logs to a remote http endpoint using the `remoteLogger` logs processor. The `remoteLogger` can be directed at a known listener such as CLE or you can create your own custom log listener. In addition, the log level in effect can be controlled remotely setting the `minLogLevelOverrideUrl` to an http address that will return the current log level to use instead of any defined default logLevel on each `logSink`. The `minLogLevelOverrideUrl` can point to a known central API such as CLE or you can create your own endpoint.\r\n\r\nThe following sections outline how to create your own endpoints for either receiving log messages from the `remoteLogger` log sink or providing the current log level override to it.\r\n\r\n**Remote log endpoint**\r\n\r\nTo implement your own remote logging endpoint, all you have to do is accept `POST` requests, with a JSON payload of an array of [log objects](#log-object), and return either `{ success: true }` or `{ success: false }`. The endpoint should always return a `200 OK` status even if your server code fails so that the CLE logger does not cause failures in the business logic in which it is embedded. A good use-case would be leveraging a serverless api platform to output logs to a queue.\r\n\r\nExample of an Azure Function that can receive requests from the `remoteLogger` log sink:\r\n\r\n```js\r\nmodule.exports = async function(context, req) {\r\n\tcontext.log(req.body); // body will have the log array\r\n\r\n\tcontext.res = {\r\n\t\tstatus: 200,\r\n\t\tbody: { success: true },\r\n\t};\r\n};\r\n```\r\n\r\n**Remote log level override**\r\n\r\nThe `minLogLevelOverrideUrl` configuration property is used to provide an endpoint that accepts `GET` requests and a `accountSid` query string parameter. `accountSid` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the `minLogLevel` configuration property. If null is returned, the `minLogLevel` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a `200 OK` status code and use the `success` property to indicate failure.\r\n\r\nExample success response:\r\n\r\n```json\r\n{\r\n\t\"success\": true,\r\n\t\"minLogLevel\": \"warn\"\r\n}\r\n```\r\n\r\nExample error response:\r\n\r\n```json\r\n{\r\n\t\"success\": false,\r\n\t\"minLogLevel\": null\r\n}\r\n```\r\n\r\n## Changelog\r\n\r\n### v2.0.7\r\n\r\n**New features**\r\n\r\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the call sid during a call, and automatically include it in `customProperties` log property for all logs that happen during that call (including Flex Action logs).\r\n\r\n### v2.0.5\r\n\r\n**New features**\r\n\r\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the worker sid and automatically include it in `customProperties` log property.\r\n\r\n**Other updates**\r\n\r\n- Updated documentation based on user feedback.\r\n\r\n### v2.0.4\r\n\r\n**Bug fixes**\r\n\r\n- Fixed issue with logging to console in Twilio Functions. The `consoleLogger` will now output the log object as serialized json.\r\n\r\n### v2.0.0 [2019-12-20]\r\n\r\n**New features**\r\n\r\n- All logging output is now configured using the `logSinks` property. The library provides two commonly used loggers - `consoleLogger` and a `remoteLogger` that accepts the log url as a parameter. [Configuration](#configuration)\r\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\r\n- Log functions have a new optional `containsPII` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\r\n- A new helper function `tagPII` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\r\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\r\n\r\n**Breaking changes**\r\n\r\n- `customLogSink` configuration renamed to `logSinks`\r\n- Removed `disableRemoteLogging` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\r\n- Removed `logToConsole` configuration. This is now configured as a log sink with a `consoleLogger` helper function.\r\n- Removed `logUrl` configuration. This is now configured as a log sink with a `remoteLogger` helper function.\r\n- Removed `minLogLevel` configuration. This is now configured per log sink.\r\n\r\n**Migration guide**\r\n\r\n```js\r\nimport cleLogger from \"@prftesp/cle-logger\";\r\n\r\n// v1.0.0 and older config\r\ncleLogger.init({\r\n\tdisableRemoteLogSinks: true, // no direct replacement\r\n\tlogToConsole: true, // replaced by the \"console logger\" sink\r\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\r\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\r\n\tcustomLogSinks: [\r\n\t\t{\r\n\t\t\tname: \"custom sink\",\r\n\t\t\tlog: logs => {\r\n\t\t\t\t/* your custom logging */\r\n\t\t\t},\r\n\t\t},\r\n\t],\r\n});\r\n\r\n// v2.0.0 config\r\ncleLogger.init({\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\tname: \"custom sink\",\r\n\t\t\tlog: logs => {\r\n\t\t\t\t/* your custom logging */\r\n\t\t\t},\r\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t\t// set to true to get feature parity with older version,\r\n\t\t\t// please consider your use case before setting\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"console logger\",\r\n\t\t\tlog: consoleLogger,\r\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t\t// set to true to get feature parity with older version,\r\n\t\t\t// please consider your use case before setting\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"remote logger\",\r\n\t\t\tlog: remoteLogger(\"http://log.output/api\"),\r\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t\t// set to true to get feature parity with older version,\r\n\t\t\t// please consider your use case before setting\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t],\r\n});\r\n```\r\n\r\n### v1.0.0 [2019-09-12]\r\n\r\n- Initial release\r\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.1.7-beta-es5-1","_nodeVersion":"10.15.2","_npmVersion":"6.11.3","dist":{"integrity":"sha512-FHFi0y30ocdQFmPB9xVQfAZcQZ4jbzw/RgvJva10as0TeBTo6iMv+tC2gXz9XzT57Hi/3/Stm+maeD+2zhafLg==","shasum":"22f087150ec8556b6721d50b814bfedf4bbabcba","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.1.7-beta-es5-1.tgz","fileCount":17,"unpackedSize":86893,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeZpKZCRA9TVsSAnZWagAAGA8P/1qfDMIT3aVc4EfxdpIV\n/w+5hFNWYZD9haOaOWXg9BnsbkVCfsImZtaqxc+2LVCoUzgQEtOxG9l1getY\nQef3hlzl3XE3xrxtK81WzFgVcUsEWJVnNeK+SRKxx5YPgyaLywbeq0Xd0jz5\npv/xKONLUUaPMmFXD/qFSgb8mGp2mIsXbvcZLzNBj1JwHFYOrb47+oKp2X2k\nutuJvWHXptPfduQY4o715oIRI97gn2+Ry5S1ZIkW8FyaOGuAnSZIcGpzX38u\nlyqiEMu3jvyrm7sxTEkEodYIQ48399tscWDKIpnhZPc5L5Vfs1NDLtykQq0o\nqQx709Ji08g0KgOXw+IA6Gv+7GDGxFpGxXQ85tKqibK/7qJLwGsLD4N7qR5L\nvCKnptRLmmGyqwaLpJ6se1PkO23OQW0gFPtFipU+jA8+uwljt6t0p7/lTIVi\ngHPpcvXUhFfrpJy1F5PbJtjAtfQSwG4UkpguunnD2wgZ70lAQFyg4lilV2JH\nzvQnpgf+5Ib7Osktq133RDQTp9NJFz83Ij3NvdIPDf6i94DAl8EU2j7J5SRS\nd/F09zUOLiiPTzFU7Em6q67C9BIzrqN/5rYQ2tb5aWMMj/XBPD9lLsGnJb6G\nbG6wmi2GKZ9ESCjOxGVxd0zGq0h4xhBOZTCxV5JDg9I9/EO/bSdtGPb39U5L\nDqSd\r\n=fsd0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDy2dV9iG8SBvcOFpUbNbFdUiV6I8FBHmHokI88xtO2gAiEAo1fsI6nW7WMWFfXqsr4fxZJ/YccQvIeLuOBFSVPRID4="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.1.7-beta-es5-1_1583780505246_0.7664190635540451"},"_hasShrinkwrap":false},"2.1.0-beta.20200327.5":{"name":"@prftesp/cle-logger","version":"2.1.0-beta.20200327.5","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build && npm run build:es5","build:es5":"rimraf dist-es5/* && tsc --build tsconfig.es5.json","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^25.1.4","@types/node":"^12.12.31","@typescript-eslint/eslint-plugin":"^2.25.0","@typescript-eslint/parser":"^2.25.0","eslint":"^6.8.0","eslint-config-prettier":"^6.10.1","eslint-plugin-prettier":"^3.1.2","jest":"^25.2.2","prettier":"^1.19.1","rimraf":"^3.0.2","ts-jest":"^25.2.1","typescript":"^3.8.3"},"dependencies":{"axios":"^0.19.2","serializerr":"^1.0.3"},"readme":"﻿CLE Logger\n==\n\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\n\n## Documentation\n\n- [Target Environment](#target-environment)\n- [Simple Example](#simple-example)\n- [Installation](#installation)\n- [Features](#features)\n- [Configuration](#configuration)\n- [API](#api)\n  - [Log sinks](#log-sinks)\n  - [Logging statements](#logging-statements)\n  - [Logging parameters](#logging-parameters)\n  - [Logging constants](#logging-constants)\n  - [Log codes](#log-codes)\n  - [Registering Twilio Actions](#registering-twilio-actions)\n  - [Twilio Event Hooks](#twilio-event-hooks)\n  - [Version](#version)\n  - [Error To Object](#error-to-object)\n- [Log Object](#log-object)\n- [Handling PII Data](#handling-pii-data)\n- [Advanced Configuration](#advanced-configuration)\n  - [Remote logging](#remote-logging)\n- [Changelog](#changelog)\n\n## Target Environment\n\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\n\n### ES5 Compatible Version\n\nIf you need an ES5 compatible version (for example you need IE11 support), use this package: https://www.npmjs.com/package/@prftesp/cle-logger-es5. **This package is not regularly released with the main library, and should be used only when absolutely necessary**. If you need to support IE11, don't forget to include the necessary polyfills, mainly for `Promise` support. To do this, you can include this script tag on the page, before you load this library `<script src=\"https://cdn.polyfill.io/v3/polyfill.min.js?features=es6\"></script>`, or visit `https://polyfill.io` for more options.\n\n**Please note** that the ES5 version is generated, and is sharing the same code and documentation as the main library. This is why the rest of the documentation doesn't mention the ES5 version in text or examples. The only difference is how you reference it (`@prftesp/cle-logger` vs `@prftesp/cle-logger-es5`). To install it use `npm install @prftesp/cle-logger-es5`, and to import it use `import { cleLogger } from \"@prftesp/cle-logger-es5\";`.\n\n## Simple Example\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"console sink\",\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\tpiiSafe: true,\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n});\n\n// will log message to console\ncleLogger.info(\"log message\");\n```\n\n## Installation\n\n```bash\n$ npm install @prftesp/cle-logger\n```\n\n## Features\n\n- Runs in Node.js and the browser\n- Remote logging\n- Multiple log sinks\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\n- Remote minimum log level overrides\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\n- Support for tagging logs with custom component names or correlation tokens\n- Send Twilio event hook data to a custom backend\n\n## Configuration\n\nTo configure the logger call the `init` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\n\n**Available options**\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// Twilio account sid, required for use with CLE\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\n\n\t// Will be associated with all logs,\n\t// if not provided, a unique uuid will be generated\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\n\n\t// An array of that will receive an array of log objects,\n\t// and can optionally return a promise. Please see the\n\t// \"Log Sinks\" section for more information.\n\tlogSinks: [\n\t\t{\n\t\t\t// name of the log sink\n\t\t\tname: \"exampleConsoleSink\",\n\t\t\t// log handler function\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\t// This flag defines if the log message is processed by this sink when the contains\n\t\t\t// PII parameter of the log statement is true. piiSafe of true indicates all log\n\t\t\t// messages are allowed (even when contains PII is true); piiSafe of false indicates\n\t\t\t// only log messages with contains PII set to false will be processed by this log sink.\n\t\t\t// Defaults to false\n\t\t\tpiiSafe: true,\n\t\t\t// log level for the sink, defaults to INFO\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n\n\t// A way to suppress error messages if set to false, defaults to true\n\tlogErrorsToConsole: true,\n\n\t// If logging Twilio Flex Actions, provide a list of actions\n\t// to be excluded from logs\n\texcludedFlexActionNames: [\"SendTyping\"],\n\n\t// A custom endpoint to fetch log level overrides\n\t// NOTE: the provided url should always end with 'accountSid=',\n\t// the library will append the accountSid\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\n\n\t// An instance of ('@twilio/flex-ui').Manager.\n\t// If provided, it will be used to get the worker sid and call sid (only during a call), and\n\t// automatically include it in 'customProperties' log property and with every log\n\ttwilioFlexManager: manager,\n\n\t// To send data from Twilio event hooks, to a custom backend,\n\t// configure one or more endpoints for each event type\n\ttwilioEventHooks: {\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\n\t},\n});\n```\n\n## API\n\nThe logger can be imported as an object in CommonJs or ES6 syntax:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n// or\nconst cleLogger = require(\"@prftesp/cle-logger\");\n```\n\nAlternatively, all top level members of the `cleLogger` are available as named exports and can be imported separately:\n\n```js\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\n\ninit({\n\tlogSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: logLevels.VERBOSE\n\t}],\n});\n\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\n\n// vs\n\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ninit({\n\tcleLogger.logSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: cleLogger.consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}],\n});\n\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\n```\n\n### Log sinks\n\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the `logSinks` config. The logSinks configuration defines an array of objects with each object defining the log sink activity. All provided log sinks will be executed in parallel. A log sink configuration must include the following properties:\n\n- `name`: this is an identifiable name for the logger and must be unique per log sink defined\n- `log`: this indicates the logic to process on each batch of logs processed by the sink. There are several built in processing functions that can be used or you can insert any code inline with this definition. The built in processing functions are `consoleLogger` which will send the log statements to the attached console or `remoteLogger` which will send the log statements to the specified url provided as a definition on the parameter (i.e. `remoteLogger(\"https://mylogger.url\"`). For a custom log processing implementation, the logic must be async operations and must return a Promise object. The custom log method will receive an array of log objects for you to handle. You can find out more about the log object type in the [Log Object](#log-object) section. Here is an example of a custom log function:\n\n```js\nlogs => {\n\t// log each received log to the console separately\n\tlogs.forEach(log => console.log(log));\n\treturn Promise.resolve();\n};\n```\n\n- `logLevel`: defines the minimum default log level that will be processed by this log sink. Log levels lower than the minimum level will not be processed by this log sink. The minimum default log level can be overridden at run time by the value returned from the overall `minLogLevelOverrideUrl` that is set up in the library `init`.\n- `piiSafe`: indicates if log messages with a `containsPII` parameter value of true should be processed by the log sink. If `containsPII` parameter value on the log message is true and piiSafe is false then the log message will not be processed by this log sink, if piiSafe is true then it will. If the `containsPII` parameter value is false on the log message than it is always processed by the log sink regardless of the piiSafe value.\n\nCustom log sink configuration example. In this example, the custom code executed for each log statement is to append the log into a file. Note how a Promise is returned as required:\n\n```js\n{\n\t// name of the log sink\n\tname: \"exampleConsoleSink\",\n\t// log handler function - this is where you put your custom handling code and\n\t// remember to always return a promise. If your operation is not asynchronous,\n\t// you can do this by returning Promise.resolve().\n\tlog: logs => {\n\t\tfs.appendFile(\n\t\t\t\"./log.txt\",\n\t\t\t`${JSON.stringify(logs)}`,\n\t\t\tconsole.log\n\t\t);\n\t\treturn Promise.resolve();\n\t},\n\t// should this log sink accept PII data, defaults to false\n\tpiiSafe: true,\n\t// log level for the sink, defaults to INFO\n\tlogLevel: cleLogger.logLevels.VERBOSE\n}\n```\n\nExample log sink configuration that uses a custom coded sink and both standard console and remote logging sinks:\n\n```js\nconst fs = require(\"fs\");\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"fileSink\",\n\t\t\tlog: logs => {\n\t\t\t\tfs.appendFile(\"./log.txt\", `${JSON.stringify(logs)}`, console.log);\n\t\t\t\treturn Promise.resolve();\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"remoteSink\",\n\t\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\n\t\t\tlogLevel: cleLogger.logLevels.WARN,\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"consoleSink\",\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n});\n```\n\n### Logging statements\n\nTo create logs, you can use one of the logging function available. They all take the same arguments, described below, and log at a corresponding log level. For example, if you use the `cleLogger.verbose` logging function, and your log sink is configured with `logLevel: cleLogger.logLevels.VERBOSE`, that log sink will receive the log. If your log sink is configured with a higher log level, for example `logLevel: cleLogger.logLevels.INFO`, then it would not receive the verbose log.\n\nHere are the available logging functions:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(...);\ncleLogger.info(...);\ncleLogger.warn(...);\ncleLogger.error(...);\ncleLogger.critical(...);\n```\n\nThe functions are all async, return `Promise<void>`, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\n\n### Logging parameters\n\nAll logging functions take the same parameters:\n\n- `message`: the only required property, no logs will be generated without it\n  - type: string\n  - required: yes\n- `additionalData`: any additional log data you would like to include with the log, can be any data type\n  - type: any\n  - required: no\n  - default value: null\n- `customProperties`: a single level deep object of important properties you would like to distinguish from general log data, like various sids CLE uses it to index these properties in Application Insights. For CLE, try to use keys found in `cleLogger.customPropertyKeys`\n  - type: object\n  - required: no\n  - default value: {}\n- `componentName`: another useful way to 'tag' your logs for easier searching. Can be any string, but for CLE, try to use available options in `cleLogger.componentNames`\n  - type: string\n  - required: no\n  - default value: \"\"\n- `logCode`: log code, can be any number used to categorize logs. For defined ranges see the [log codes](#log-codes) section.\n  - type: number\n  - required: no\n  - default value: 0\n- `correlationToken`: correlation token to be associated with this log only\n  - type: string\n  - required: no\n  - default value: token provided in `init`, or a generated token if one wasn't provided\n- `containsPII`: does the log contain PII data, defaults to false. If set to true, the log will be passed in only to those log sinks that have `piiSafe` set to true. For more info see the [handling PII data](#handling-pii-data) section.\n  - type: boolean\n  - required: no\n  - default value: false\n\nExample verbose logging statement:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(\n\t\"Log message\", // message\n\t{ data: \"test\" }, // additionalData\n\t{ [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" }, // customProperties\n\tcleLogger.componentNames.SESSION, // componentName\n\t70123, // logCode\n\t\"my-token\", // correlationToken\n\ttrue, // containsPII\n);\n```\n\n### Logging constants\n\n**Logging levels**\n\nLogging level constants should be used when configuring the log level of a [log sink](#log-sinks).\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.logLevels.VERBOSE; // verbose\ncleLogger.logLevels.INFO; // info\ncleLogger.logLevels.WARN; // warn\ncleLogger.logLevels.ERROR; // error\ncleLogger.logLevels.CRITICAL; // critical\n```\n\n**Component names**\n\nDefines a set of commonly used components that should be used for the `componentName` parameter value of a log statement if you are logging from one of these common components in a Twilio implementation.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.componentNames.SESSION;\ncleLogger.componentNames.DIALER;\ncleLogger.componentNames.DASHBOARD;\ncleLogger.componentNames.AFTERCALL;\ncleLogger.componentNames.SUPERVISOR;\n```\n\n**Custom property keys**\n\nDefines a set of commonly used key that should be used when defining the custom properties object that is supplied in the `customProperties` parameter of any logging statement.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.customPropertyKeys.CHANNEL_SID;\ncleLogger.customPropertyKeys.TASK_SID;\ncleLogger.customPropertyKeys.CALL_SID;\ncleLogger.customPropertyKeys.WORKFLOW_SID;\ncleLogger.customPropertyKeys.WORKER_SID;\n```\n\n### Log codes\n\nIt can be helpful when investigating issues to only view logs related to specific areas of a system. Use Log Codes to give a category definition for what area of the contact center the log activity occurred within. Use numbers within the following pre-defined log code ranges or define custom log codes by using your own numbers higher than 300,000.\n\n- 100 000 - 109 999 => CALL_STATUS\n- 110 000 - 119 999 => IVR\n- 120 000 - 129 999 => ROUTING\n- 130 000 - 139 999 => AGENT_ACTION\n- 140 000 - 149 999 => TRANSFER\n- 150 000 - 159 999 => RM_INTEGRATION\n- 160 000 - 169 999 => WFM_INTEGRATION\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\n\n### Registering Twilio Actions\n\nIt is important to log information from the Twilio Flex UI for all implementations even when there is little customization happening. Many developers find it useful to generate log activity when some, or any, of the Twilio Actions occur within the Twilio Flex UI. Twilio Actions are events that arise from the Twilio Flex UI base implementation and indicate when certain activity is occurring - such as an agent hanging up a call. The CLE Logger library can be setup to automatically register all, or selected, Twilio Actions such that right after any code tied to the Action event has been executed a log statement is written indicating the Action occurred. This log statement will also automatically include the action event payload. To enable this logging capability, use the following approach to register the Twilio Actions. This only needs to be performed once during the initialization of the Twilio Flex UI page on the client.\n\nExample in a Twilio Flex UI plugin:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n/**\n * @param flex { typeof import('@twilio/flex-ui') }\n * @param manager { import('@twilio/flex-ui').Manager }\n */\ninit(flex, manager) {\n\tcleLogger.init({\n\t\taccountSid: \"your-account-sid\"\n\t});\n\n\tcleLogger.registerTwilioActions(flex);\n}\n```\n\n**Note**: A Twilio account sid must be provided during `init` with the `accountSid` parameter, otherwise the actions won't be logged.\n\nThe logger will loop through all actions found under `flex.Actions.actions`, and attach `after` event handlers to log the action name and payload. Any private properties found in the payload (starting with '\\_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Not all Action events need to be logged and by using the `excludedFlexActionNames` property in the logger configuration the Action events not desired can be ignored. Any actions names configured in `excludedFlexActionNames` will not be included in the logs.\n\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\n\n**Twilio Actions log levels**\n\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of `INFO`.\n\n```js\nAcceptTask: INFO;\nCancelTransfer: INFO;\nCompleteTask: INFO;\nHangupCall: INFO;\nHideDirectory: VERBOSE;\nHistoryGo: VERBOSE;\nHistoryGoBack: VERBOSE;\nHistoryGoForward: VERBOSE;\nHistoryPush: VERBOSE;\nHistoryReplace: VERBOSE;\nHoldCall: INFO;\nHoldParticipant: INFO;\nKickParticipant: INFO;\nLogout: INFO;\nMonitorCall: INFO;\nNavigateToView: INFO;\nRejectTask: INFO;\nSelectTask: INFO;\nSelectTaskInSupervisor: INFO;\nSelectWorkerInSupervisor: INFO;\nSendMessage: INFO;\nSendTyping: VERBOSE;\nSetActivity: INFO;\nSetInputText: VERBOSE;\nShowDirectory: VERBOSE;\nStopMonitoringCall: INFO;\nToggleMute: INFO;\nToggleSidebar: VERBOSE;\nTransferTask: INFO;\nUnholdCall: INFO;\nUnholdParticipant: INFO;\nWrapupTask: INFO;\n```\n\n### Twilio Event Hooks\n\nCurrently, the Twilio console only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with `twilioEventHooks` option, and use the following functions to send event data to a custom endpoint.\n\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is `application/x-www-form-urlencoded` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// unrelated init properties omitted from this example.\n\t// include in the twilioEventHooks array an entry for only the event hooks\n\t// the executing code is going to relay. For example, if the code is only\n\t// handling task router web hook events then you would only need to define\n\t// the taskRouter relay url here.\n\ttwilioEventHooks: {\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\n\t},\n});\n\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\n```\n\nExample Azure Function hook for debugger events:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\nmodule.exports = async function(context, req) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\t},\n\t});\n\n\t// log to console\n\tcleLogger.info(\"Received Twilio debugger event\");\n\n\t// send event data to a configured url\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\n};\n```\n\n**NOTE WHEN USING THIS FUNCTIONALITY IN TWILIO FUNCTIONS:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to `x-www-form-urlencoded` format.\n\nExample Twilio function task router relay using the `form-urlencoded` npm package to convert the data to `x-www-form-urlencoded` format:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\nconst formurlencoded = require(\"form-urlencoded\").default;\n\nexports.handler = async function(context, event, callback) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\t},\n\t});\n\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\n\n\tcallback();\n};\n```\n\n### Version\n\nVersion will be automatically included in all logs.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.version; // 1.0.0\n```\n\n### Error To Object\n\nUtility function to convert an `Error` object to a plain JS object, with all properties preserved. When serialized, `Error` objects will omit custom properties from the output which makes debugging difficult. This utility will traverse all properties in the `Error` object, and output a plain JS object with all properties including the stack trace. See an example below.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\nconst err = new Error(\"error message\");\nconsole.log(err); // \"Error: error message\\n    at Object.<anonymous> …\"\n\nconst errObject = cleLogger.errorToObject(err);\nconsole.log(errObjct); // {name: 'Error', message: 'error message', stack: 'Error: error message\\n    at Object.<anonymous> …'}\n```\n\nExample usage when logging errors:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ntry {\n\t// some code that can throw an error\n} catch (error) {\n\tcleLogger.error(\"My error message\", cleLogger.errorToObject(error));\n}\n```\n\n## Log Object\n\nThe following is the type definition of the log object received by [log sinks](#log-sinks). It consists of properties provided in log messages, as well as some that are automatically inserted like the `eventDate`. You can use this information to customize your logs inside a custom log sink (e.g. filter out unwanted logs, modify some of the properties to include more data, or any kind of conditional logic based on the value of these properties). If using the built in `remoteLogger` log processor with your custom backend, you can expect an array of these objects to be sent to your log endpoint. For more info about implementing a custom logging endpoint see the [remote logging](#remote-logging) section.\n\n```js\n{\n\t// Account sid if provided during init\n\taccountSid: string;\n\n\t// Any additional log data to be included with the log\n\tadditionalData: unknown;\n\n\t// Component name, if provided\n\tcomponentName: string;\n\n\t// Generated or configured token\n\tcorrelationToken: string;\n\n\t// Current UTC datetime in ISO 8601 format\n\teventDate: string;\n\n\t// Log level\n\tlevel: string;\n\n\t// Log code, if provided\n\tlogCode: number;\n\n\t// Log message\n\tmessage: string;\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\tproperties: object;\n\n\t// BrowserFunction if the environment is the browser\n\t// CloudFunction if the environment is Node.js\n\tsource: string;\n\n\t// Indicates that the log contains PII data\n\tcontainsPII: boolean;\n\n\t// Logging library version\n\tloggerVersion: string;\n}\n```\n\n## Handling PII Data\n\nThe logger provides support for helping to protect log messages or data that need to include PII (Personally Identifiable Information). While the logger has several mechanisms that can be used to flag logs that contain PII and block PII from going to particular log sinks, it is still ultimately the developers responsibility to ensure PII is flagged or marked appropriately for the logger to know when it exists - the logger cannot automatically detect if PII is part of a log statement or data object. The general guideline is that developers should avoid including any PII data in log information whenever possible. In the instances where PII data must be included there are several mechanisms the logger supports to help indicate PII data and protect where it is sent.\n\nA `containsPII` parameter exists on log statement methods. This flag should be set to true if there is PII data, or any potential chance of it, in the log message, additional data or custom properties data. When this flag is set to true then it will only allow log sinks that have the `piiSafe` configuration set to true to receive and process the log message. `piiSafe` indicates on a log sink whether that log sink receiver is able to internally store PII data OR has logic to strip PII data that is tagged in the message or from pre-configured additional data fields before the log is written to any storage or output device. When sending data to any `remoteLogger` endpoint the log sink can be marked `piiSafe` only as long as data is appropriately tagged in messages and/or the names of custom properties that might have PII values in additional data are pre-configured AND the endpoint implementer has verified and ensured their receiving code will remove marked PII data immediately before sending logs to any output or storage media.\n\nTo tag any part of the message parameter in a log statement as containing PII within the actual log message use the `tagPII` utility. The `tagPII` utility will indicate to a remote backend that the text between the tags should be stripped or masked before sending logs to any output or storage media.\n\nExample tagged output:\n\n```js\nimport { tagPII } from \"@prftesp/cle-logger\";\n\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\n```\n\n## Advanced Configuration\n\n### Remote logging\n\nThe library provides a built in way to transmit logs to a remote http endpoint using the `remoteLogger` logs processor. The `remoteLogger` can be directed at a known listener such as CLE or you can create your own custom log listener. In addition, the log level in effect can be controlled remotely setting the `minLogLevelOverrideUrl` to an http address that will return the current log level to use instead of any defined default logLevel on each `logSink`. The `minLogLevelOverrideUrl` can point to a known central API such as CLE or you can create your own endpoint.\n\nThe following sections outline how to create your own endpoints for either receiving log messages from the `remoteLogger` log sink or providing the current log level override to it.\n\n**Remote log endpoint**\n\nTo implement your own remote logging endpoint, all you have to do is accept `POST` requests, with a JSON payload of an array of [log objects](#log-object), and return either `{ success: true }` or `{ success: false }`. The endpoint should always return a `200 OK` status even if your server code fails so that the CLE logger does not cause failures in the business logic in which it is embedded. A good use-case would be leveraging a serverless api platform to output logs to a queue.\n\nExample of an Azure Function that can receive requests from the `remoteLogger` log sink:\n\n```js\nmodule.exports = async function(context, req) {\n\tcontext.log(req.body); // body will have the log array\n\n\tcontext.res = {\n\t\tstatus: 200,\n\t\tbody: { success: true },\n\t};\n};\n```\n\n**Remote log level override**\n\nThe `minLogLevelOverrideUrl` configuration property is used to provide an endpoint that accepts `GET` requests and a `accountSid` query string parameter. `accountSid` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the `minLogLevel` configuration property. If null is returned, the `minLogLevel` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a `200 OK` status code and use the `success` property to indicate failure.\n\nExample success response:\n\n```json\n{\n\t\"success\": true,\n\t\"minLogLevel\": \"warn\"\n}\n```\n\nExample error response:\n\n```json\n{\n\t\"success\": false,\n\t\"minLogLevel\": null\n}\n```\n\n## Changelog\n\n### v2.1.0\n\n**New features**\n\n- Added [Error To Object](#error-to-object) method to assist users in converting error object to plain objects.\n\n### v2.0.7\n\n**New features**\n\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the call sid during a call, and automatically include it in `customProperties` log property for all logs that happen during that call (including Flex Action logs).\n\n### v2.0.5\n\n**New features**\n\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the worker sid and automatically include it in `customProperties` log property.\n\n**Other updates**\n\n- Updated documentation based on user feedback.\n\n### v2.0.4\n\n**Bug fixes**\n\n- Fixed issue with logging to console in Twilio Functions. The `consoleLogger` will now output the log object as serialized json.\n\n### v2.0.0 [2019-12-20]\n\n**New features**\n\n- All logging output is now configured using the `logSinks` property. The library provides two commonly used loggers - `consoleLogger` and a `remoteLogger` that accepts the log url as a parameter. [Configuration](#configuration)\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\n- Log functions have a new optional `containsPII` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\n- A new helper function `tagPII` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\n\n**Breaking changes**\n\n- `customLogSink` configuration renamed to `logSinks`\n- Removed `disableRemoteLogging` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\n- Removed `logToConsole` configuration. This is now configured as a log sink with a `consoleLogger` helper function.\n- Removed `logUrl` configuration. This is now configured as a log sink with a `remoteLogger` helper function.\n- Removed `minLogLevel` configuration. This is now configured per log sink.\n\n**Migration guide**\n\n```js\nimport cleLogger from \"@prftesp/cle-logger\";\n\n// v1.0.0 and older config\ncleLogger.init({\n\tdisableRemoteLogSinks: true, // no direct replacement\n\tlogToConsole: true, // replaced by the \"console logger\" sink\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\n\tcustomLogSinks: [\n\t\t{\n\t\t\tname: \"custom sink\",\n\t\t\tlog: logs => {\n\t\t\t\t/* your custom logging */\n\t\t\t},\n\t\t},\n\t],\n});\n\n// v2.0.0 config\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"custom sink\",\n\t\t\tlog: logs => {\n\t\t\t\t/* your custom logging */\n\t\t\t},\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"console logger\",\n\t\t\tlog: consoleLogger,\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"remote logger\",\n\t\t\tlog: remoteLogger(\"http://log.output/api\"),\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t],\n});\n```\n\n### v1.0.0 [2019-09-12]\n\n- Initial release\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.1.0-beta.20200327.5","_nodeVersion":"12.16.1","_npmVersion":"6.14.2","dist":{"integrity":"sha512-jXWpSR5cc1Hkmq7ZJXZjBQmuCXDliWgWUTjL7adZdlPr/k1G0UAe0n6trzqY+E01z8Skcvr+jUyvghfFmB6xEg==","shasum":"c7725c0acf214c3d25dc2adea25074fb1eeb9780","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.1.0-beta.20200327.5.tgz","fileCount":17,"unpackedSize":80536,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJefjdBCRA9TVsSAnZWagAAUq0P/3wFHHc46vOSqcPuAxBM\n17NOeyXnbGQ38yr8RY3F02G1nEWzsLUym+kcQUM+TsvY9pwh5/oXWgLDA1TR\nOPn5PHZTZytdWvDTNzskDKbfZHUTlvl9TxJFSbFOWjzV2VgfuE+MEHJhxYRz\ncC9pMVnjVdG6MSyd90EccBiW1viA5MOMXKnliRPNJRfd010TGUkwQKyhfiIl\n3eC5aibsdAS2ExqAseESjntOWDVpN5q+CFYqN1T6VvlFpZf2mDzpsFSLlP3H\nEJXJaNlzdJCen7Qlo1KS5mwtleTrnvd+R34viiq5Q5DLuhlidJiMiHXa1aHn\nXxuwgb9AOPjmzmVRVI6l8T90Ivik4bpxQg2spW8CDhFeKHew6/r0xyuWP+A2\nWkX5lOXkkWQXsdaaTQOWw2o20utoMLSuWHtm+l0zq0eNFBn3FnqwXxau9aNK\n8FUvrRrCIHnqAkf4XS2RKKR4OMNx7V71UDZHsYZh1GtkVRATVcWkEF2qfNf6\nylEgUetMnbVIACbm+QzAqtCG/IzrEzPyMJgVgiZcNSS6vT+F6JBoJwVn0NS5\nZp+c0UYJpTPQf9FVWakOWvN9JaJj+m1OPECV8QLxzlxVHw0NkcBM/yqOM9Iy\ne6qJ7r97fABHSQk3dFESbhsYpADmHruDOKS7TDJH7QZ5IDlvpGCr3BAAXeZJ\n/0O5\r\n=pcQn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFt2YFFOekL/5T93RTsC44Q1NyXVbOH6oaS0MAnzfMguAiB/ppxPbHF/fuDerT+F4G0EwFGwZeqfhnBoa6V4Al/JPw=="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.1.0-beta.20200327.5_1585329984507_0.06077404324316005"},"_hasShrinkwrap":false},"2.1.0":{"name":"@prftesp/cle-logger","version":"2.1.0","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build && npm run build:es5","build:es5":"rimraf dist-es5/* && tsc --build tsconfig.es5.json","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^25.1.4","@types/node":"^12.12.31","@typescript-eslint/eslint-plugin":"^2.25.0","@typescript-eslint/parser":"^2.25.0","eslint":"^6.8.0","eslint-config-prettier":"^6.10.1","eslint-plugin-prettier":"^3.1.2","jest":"^25.2.2","prettier":"^1.19.1","rimraf":"^3.0.2","ts-jest":"^25.2.1","typescript":"^3.8.3"},"dependencies":{"axios":"^0.19.2","serializerr":"^1.0.3"},"_id":"@prftesp/cle-logger@2.1.0","_nodeVersion":"12.16.1","_npmVersion":"6.14.2","dist":{"integrity":"sha512-n81hVior4fkIs0q7OfUxedx8/s4yjDWOnTgs+X0X/Z+OQwRvBvV1sJsukJrV2ZM88k5Ex9cdoOJbPxqNl/Y9tQ==","shasum":"e39dee72c57638d43f7295c4391406b1ebe0bf65","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.1.0.tgz","fileCount":17,"unpackedSize":80433,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJefjegCRA9TVsSAnZWagAAMMMP/jXkU6nHmC87Zvk/5/NZ\nNsbHav8B4zrlJW4VLOAhdeTTyKQ6YNhgZH8lufg3FTRYzIngABBgi6mFsn5V\n33hRM6OTINSYm7s9wCD536mZPXCaIehlpqtwef9wQjIVWluomXrLYZk1pr2P\nCSixcBJAub+AL0J98zl8433slf831DGEb/K8E1slkpH6L5q9TNNszMaq2Ahi\n1/haCeco2sF/yNjGzPUDRs+Uvqi+L/xGdfR8h/jFgKddq7idSS89J/CxjSHj\nJOvzByvBREACNCqZxlpGhMHH8LjxekAgWcZXE8qsjZ2dn0o9+J0RCohU1tCN\nquH3nKRUiye1fsxnt3RD2aQOhIRfI7z9EmlpjdPPv7yxL0YhNtQGXRQ0eeC9\naAvpFJg67eJxGmcLVGteUbxwqIBRPGt8+3S5owmaEM6Rbh26BJPFAkW2odjl\nYXMVtZKLlJkDeHyiuGDd+vb+tdASNt6LxPhEm32MCYbnf3+nFwRjwPucH40m\n4VH0R1IE4GKDH6871mb91S5CpRNWQE8GCf4bkGmzUKDLJeGR/BAjFBuA/vpZ\nxC7sIIhhgnoIUAx7kJ2vbM1L0CIcOz8tKIAhXjfTl8if+DPvRVgdSScbA28s\nYejc6SYgbk+H9oHvolEcWA5zZtNpw/TqJybAr/aLcT3tqROZMsNWPrrdNL/H\nCRg2\r\n=WfHl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDYUymgp+h2r+hZRy0IXTPC46Y//5wH2UhANJSS+uZylgIgDU3gcCawJUsi4kjfRyqYgY3oTgSh9aKvI3cdmxRMcGs="}]},"maintainers":[{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.1.0_1585330079517_0.033481371292219064"},"_hasShrinkwrap":false},"2.1.1-beta.0":{"name":"@prftesp/cle-logger","version":"2.1.1-beta.0","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build && npm run build:es5","build:es5":"rimraf dist-es5/* && tsc --build tsconfig.es5.json","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^25.1.4","@types/node":"^12.12.31","@typescript-eslint/eslint-plugin":"^2.25.0","@typescript-eslint/parser":"^2.25.0","eslint":"^6.8.0","eslint-config-prettier":"^6.10.1","eslint-plugin-prettier":"^3.1.2","jest":"^25.2.2","prettier":"^1.19.1","rimraf":"^3.0.2","ts-jest":"^25.2.1","typescript":"^3.8.3"},"dependencies":{"axios":"^0.19.2","serializerr":"^1.0.3"},"readme":"﻿CLE Logger\r\n==\r\n\r\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\r\n\r\n## Documentation\r\n\r\n- [Target Environment](#target-environment)\r\n- [Simple Example](#simple-example)\r\n- [Installation](#installation)\r\n- [Features](#features)\r\n- [Configuration](#configuration)\r\n- [API](#api)\r\n  - [Log sinks](#log-sinks)\r\n  - [Logging statements](#logging-statements)\r\n  - [Logging parameters](#logging-parameters)\r\n  - [Logging constants](#logging-constants)\r\n  - [Log codes](#log-codes)\r\n  - [Registering Twilio Actions](#registering-twilio-actions)\r\n  - [Twilio Event Hooks](#twilio-event-hooks)\r\n  - [Version](#version)\r\n  - [Error To Object](#error-to-object)\r\n- [Log Object](#log-object)\r\n- [Handling PII Data](#handling-pii-data)\r\n- [Advanced Configuration](#advanced-configuration)\r\n  - [Remote logging](#remote-logging)\r\n- [Changelog](#changelog)\r\n\r\n## Target Environment\r\n\r\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\r\n\r\n### ES5 Compatible Version\r\n\r\nIf you need an ES5 compatible version (for example you need IE11 support), use this package: https://www.npmjs.com/package/@prftesp/cle-logger-es5. **This package is not regularly released with the main library, and should be used only when absolutely necessary**. If you need to support IE11, don't forget to include the necessary polyfills, mainly for `Promise` support. To do this, you can include this script tag on the page, before you load this library `<script src=\"https://cdn.polyfill.io/v3/polyfill.min.js?features=es6\"></script>`, or visit `https://polyfill.io` for more options.\r\n\r\n**Please note** that the ES5 version is generated, and is sharing the same code and documentation as the main library. This is why the rest of the documentation doesn't mention the ES5 version in text or examples. The only difference is how you reference it (`@prftesp/cle-logger` vs `@prftesp/cle-logger-es5`). To install it use `npm install @prftesp/cle-logger-es5`, and to import it use `import { cleLogger } from \"@prftesp/cle-logger-es5\";`.\r\n\r\n## Simple Example\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\tname: \"console sink\",\r\n\t\t\tlog: cleLogger.consoleLogger,\r\n\t\t\tpiiSafe: true,\r\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\r\n\t\t},\r\n\t],\r\n});\r\n\r\n// will log message to console\r\ncleLogger.info(\"log message\");\r\n```\r\n\r\n## Installation\r\n\r\n```bash\r\n$ npm install @prftesp/cle-logger\r\n```\r\n\r\n## Features\r\n\r\n- Runs in Node.js and the browser\r\n- Remote logging\r\n- Multiple log sinks\r\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\r\n- Remote minimum log level overrides\r\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\r\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\r\n- Support for tagging logs with custom component names or correlation tokens\r\n- Send Twilio event hook data to a custom backend\r\n\r\n## Configuration\r\n\r\nTo configure the logger call the `init` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\r\n\r\n**Available options**\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// Twilio account sid, required for use with CLE\r\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\r\n\r\n\t// Will be associated with all logs,\r\n\t// if not provided, a unique uuid will be generated\r\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\r\n\t// An array of that will receive an array of log objects,\r\n\t// and can optionally return a promise. Please see the\r\n\t// \"Log Sinks\" section for more information.\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\t// name of the log sink\r\n\t\t\tname: \"exampleConsoleSink\",\r\n\t\t\t// log handler function\r\n\t\t\tlog: cleLogger.consoleLogger,\r\n\t\t\t// This flag defines if the log message is processed by this sink when the contains\r\n\t\t\t// PII parameter of the log statement is true. piiSafe of true indicates all log\r\n\t\t\t// messages are allowed (even when contains PII is true); piiSafe of false indicates\r\n\t\t\t// only log messages with contains PII set to false will be processed by this log sink.\r\n\t\t\t// Defaults to false\r\n\t\t\tpiiSafe: true,\r\n\t\t\t// log level for the sink, defaults to INFO\r\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\r\n\t\t},\r\n\t],\r\n\r\n\t// A way to suppress error messages if set to false, defaults to true\r\n\tlogErrorsToConsole: true,\r\n\r\n\t// If logging Twilio Flex Actions, provide a list of actions\r\n\t// to be excluded from logs\r\n\texcludedFlexActionNames: [\"SendTyping\"],\r\n\r\n\t// A custom endpoint to fetch log level overrides\r\n\t// NOTE: the provided url should always end with 'accountSid=',\r\n\t// the library will append the accountSid\r\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\r\n\r\n\t// An instance of ('@twilio/flex-ui').Manager.\r\n\t// If provided, it will be used to get the worker sid and call sid (only during a call), and\r\n\t// automatically include it in 'customProperties' log property and with every log\r\n\ttwilioFlexManager: manager,\r\n\r\n\t// To send data from Twilio event hooks, to a custom backend,\r\n\t// configure one or more endpoints for each event type\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\r\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\r\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\r\n\t},\r\n});\r\n```\r\n\r\n## API\r\n\r\nThe logger can be imported as an object in CommonJs or ES6 syntax:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n// or\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n```\r\n\r\nAlternatively, all top level members of the `cleLogger` are available as named exports and can be imported separately:\r\n\r\n```js\r\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tlogSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\r\n\r\n// vs\r\n\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tcleLogger.logSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\r\n```\r\n\r\n### Log sinks\r\n\r\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the `logSinks` config. The logSinks configuration defines an array of objects with each object defining the log sink activity. All provided log sinks will be executed in parallel. A log sink configuration must include the following properties:\r\n\r\n- `name`: this is an identifiable name for the logger and must be unique per log sink defined\r\n- `log`: this indicates the logic to process on each batch of logs processed by the sink. There are several built in processing functions that can be used or you can insert any code inline with this definition. The built in processing functions are `consoleLogger` which will send the log statements to the attached console or `remoteLogger` which will send the log statements to the specified url provided as a definition on the parameter (i.e. `remoteLogger(\"https://mylogger.url\"`). For a custom log processing implementation, the logic must be async operations and must return a Promise object. The custom log method will receive an array of log objects for you to handle. You can find out more about the log object type in the [Log Object](#log-object) section. Here is an example of a custom log function:\r\n\r\n```js\r\nlogs => {\r\n\t// log each received log to the console separately\r\n\tlogs.forEach(log => console.log(log));\r\n\treturn Promise.resolve();\r\n};\r\n```\r\n\r\n- `logLevel`: defines the minimum default log level that will be processed by this log sink. Log levels lower than the minimum level will not be processed by this log sink. The minimum default log level can be overridden at run time by the value returned from the overall `minLogLevelOverrideUrl` that is set up in the library `init`.\r\n- `piiSafe`: indicates if log messages with a `containsPII` parameter value of true should be processed by the log sink. If `containsPII` parameter value on the log message is true and piiSafe is false then the log message will not be processed by this log sink, if piiSafe is true then it will. If the `containsPII` parameter value is false on the log message than it is always processed by the log sink regardless of the piiSafe value.\r\n\r\nCustom log sink configuration example. In this example, the custom code executed for each log statement is to append the log into a file. Note how a Promise is returned as required:\r\n\r\n```js\r\n{\r\n\t// name of the log sink\r\n\tname: \"exampleConsoleSink\",\r\n\t// log handler function - this is where you put your custom handling code and\r\n\t// remember to always return a promise. If your operation is not asynchronous,\r\n\t// you can do this by returning Promise.resolve().\r\n\tlog: logs => {\r\n\t\tfs.appendFile(\r\n\t\t\t\"./log.txt\",\r\n\t\t\t`${JSON.stringify(logs)}`,\r\n\t\t\tconsole.log\r\n\t\t);\r\n\t\treturn Promise.resolve();\r\n\t},\r\n\t// should this log sink accept PII data, defaults to false\r\n\tpiiSafe: true,\r\n\t// log level for the sink, defaults to INFO\r\n\tlogLevel: cleLogger.logLevels.VERBOSE\r\n}\r\n```\r\n\r\nExample log sink configuration that uses a custom coded sink and both standard console and remote logging sinks:\r\n\r\n```js\r\nconst fs = require(\"fs\");\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\ncleLogger.init({\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\tname: \"fileSink\",\r\n\t\t\tlog: logs => {\r\n\t\t\t\tfs.appendFile(\"./log.txt\", `${JSON.stringify(logs)}`, console.log);\r\n\t\t\t\treturn Promise.resolve();\r\n\t\t\t},\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"remoteSink\",\r\n\t\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\r\n\t\t\tlogLevel: cleLogger.logLevels.WARN,\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"consoleSink\",\r\n\t\t\tlog: cleLogger.consoleLogger,\r\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\r\n\t\t},\r\n\t],\r\n});\r\n```\r\n\r\n### Logging statements\r\n\r\nTo create logs, you can use one of the logging function available. They all take the same arguments, described below, and log at a corresponding log level. For example, if you use the `cleLogger.verbose` logging function, and your log sink is configured with `logLevel: cleLogger.logLevels.VERBOSE`, that log sink will receive the log. If your log sink is configured with a higher log level, for example `logLevel: cleLogger.logLevels.INFO`, then it would not receive the verbose log.\r\n\r\nHere are the available logging functions:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(...);\r\ncleLogger.info(...);\r\ncleLogger.warn(...);\r\ncleLogger.error(...);\r\ncleLogger.critical(...);\r\n```\r\n\r\nThe functions are all async, return `Promise<void>`, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\r\n\r\n### Logging parameters\r\n\r\nAll logging functions take the same parameters:\r\n\r\n- `message`: the only required property, no logs will be generated without it\r\n  - type: string\r\n  - required: yes\r\n- `additionalData`: any additional log data you would like to include with the log, can be any data type\r\n  - type: any\r\n  - required: no\r\n  - default value: null\r\n- `customProperties`: a single level deep object of important properties you would like to distinguish from general log data, like various sids CLE uses it to index these properties in Application Insights. For CLE, try to use keys found in `cleLogger.customPropertyKeys`\r\n  - type: object\r\n  - required: no\r\n  - default value: {}\r\n- `componentName`: another useful way to 'tag' your logs for easier searching. Can be any string, but for CLE, try to use available options in `cleLogger.componentNames`\r\n  - type: string\r\n  - required: no\r\n  - default value: \"\"\r\n- `logCode`: log code, can be any number used to categorize logs. For defined ranges see the [log codes](#log-codes) section.\r\n  - type: number\r\n  - required: no\r\n  - default value: 0\r\n- `correlationToken`: correlation token to be associated with this log only\r\n  - type: string\r\n  - required: no\r\n  - default value: token provided in `init`, or a generated token if one wasn't provided\r\n- `containsPII`: does the log contain PII data, defaults to false. If set to true, the log will be passed in only to those log sinks that have `piiSafe` set to true. For more info see the [handling PII data](#handling-pii-data) section.\r\n  - type: boolean\r\n  - required: no\r\n  - default value: false\r\n\r\nExample verbose logging statement:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(\r\n\t\"Log message\", // message\r\n\t{ data: \"test\" }, // additionalData\r\n\t{ [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" }, // customProperties\r\n\tcleLogger.componentNames.SESSION, // componentName\r\n\t70123, // logCode\r\n\t\"my-token\", // correlationToken\r\n\ttrue, // containsPII\r\n);\r\n```\r\n\r\n### Logging constants\r\n\r\n**Logging levels**\r\n\r\nLogging level constants should be used when configuring the log level of a [log sink](#log-sinks).\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.logLevels.VERBOSE; // verbose\r\ncleLogger.logLevels.INFO; // info\r\ncleLogger.logLevels.WARN; // warn\r\ncleLogger.logLevels.ERROR; // error\r\ncleLogger.logLevels.CRITICAL; // critical\r\n```\r\n\r\n**Component names**\r\n\r\nDefines a set of commonly used components that should be used for the `componentName` parameter value of a log statement if you are logging from one of these common components in a Twilio implementation.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.componentNames.SESSION;\r\ncleLogger.componentNames.DIALER;\r\ncleLogger.componentNames.DASHBOARD;\r\ncleLogger.componentNames.AFTERCALL;\r\ncleLogger.componentNames.SUPERVISOR;\r\n```\r\n\r\n**Custom property keys**\r\n\r\nDefines a set of commonly used key that should be used when defining the custom properties object that is supplied in the `customProperties` parameter of any logging statement.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.customPropertyKeys.CHANNEL_SID;\r\ncleLogger.customPropertyKeys.TASK_SID;\r\ncleLogger.customPropertyKeys.CALL_SID;\r\ncleLogger.customPropertyKeys.WORKFLOW_SID;\r\ncleLogger.customPropertyKeys.WORKER_SID;\r\n```\r\n\r\n### Log codes\r\n\r\nIt can be helpful when investigating issues to only view logs related to specific areas of a system. Use Log Codes to give a category definition for what area of the contact center the log activity occurred within. Use numbers within the following pre-defined log code ranges or define custom log codes by using your own numbers higher than 300,000.\r\n\r\n- 100 000 - 109 999 => CALL_STATUS\r\n- 110 000 - 119 999 => IVR\r\n- 120 000 - 129 999 => ROUTING\r\n- 130 000 - 139 999 => AGENT_ACTION\r\n- 140 000 - 149 999 => TRANSFER\r\n- 150 000 - 159 999 => RM_INTEGRATION\r\n- 160 000 - 169 999 => WFM_INTEGRATION\r\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\r\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\r\n\r\n### Registering Twilio Actions\r\n\r\nIt is important to log information from the Twilio Flex UI for all implementations even when there is little customization happening. Many developers find it useful to generate log activity when some, or any, of the Twilio Actions occur within the Twilio Flex UI. Twilio Actions are events that arise from the Twilio Flex UI base implementation and indicate when certain activity is occurring - such as an agent hanging up a call. The CLE Logger library can be setup to automatically register all, or selected, Twilio Actions such that right after any code tied to the Action event has been executed a log statement is written indicating the Action occurred. This log statement will also automatically include the action event payload. To enable this logging capability, use the following approach to register the Twilio Actions. This only needs to be performed once during the initialization of the Twilio Flex UI page on the client.\r\n\r\nExample in a Twilio Flex UI plugin:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\n/**\r\n * @param flex { typeof import('@twilio/flex-ui') }\r\n * @param manager { import('@twilio/flex-ui').Manager }\r\n */\r\ninit(flex, manager) {\r\n\tcleLogger.init({\r\n\t\taccountSid: \"your-account-sid\"\r\n\t});\r\n\r\n\tcleLogger.registerTwilioActions(flex);\r\n}\r\n```\r\n\r\n**Note**: A Twilio account sid must be provided during `init` with the `accountSid` parameter, otherwise the actions won't be logged.\r\n\r\nThe logger will loop through all actions found under `flex.Actions.actions`, and attach `after` event handlers to log the action name and payload. Any private properties found in the payload (starting with '\\_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Not all Action events need to be logged and by using the `excludedFlexActionNames` property in the logger configuration the Action events not desired can be ignored. Any actions names configured in `excludedFlexActionNames` will not be included in the logs.\r\n\r\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\r\n\r\n**Twilio Actions log levels**\r\n\r\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of `INFO`.\r\n\r\n```js\r\nAcceptTask: INFO;\r\nCancelTransfer: INFO;\r\nCompleteTask: INFO;\r\nHangupCall: INFO;\r\nHideDirectory: VERBOSE;\r\nHistoryGo: VERBOSE;\r\nHistoryGoBack: VERBOSE;\r\nHistoryGoForward: VERBOSE;\r\nHistoryPush: VERBOSE;\r\nHistoryReplace: VERBOSE;\r\nHoldCall: INFO;\r\nHoldParticipant: INFO;\r\nKickParticipant: INFO;\r\nLogout: INFO;\r\nMonitorCall: INFO;\r\nNavigateToView: INFO;\r\nRejectTask: INFO;\r\nSelectTask: INFO;\r\nSelectTaskInSupervisor: INFO;\r\nSelectWorkerInSupervisor: INFO;\r\nSendMessage: INFO;\r\nSendTyping: VERBOSE;\r\nSetActivity: INFO;\r\nSetInputText: VERBOSE;\r\nShowDirectory: VERBOSE;\r\nStopMonitoringCall: INFO;\r\nToggleMute: INFO;\r\nToggleSidebar: VERBOSE;\r\nTransferTask: INFO;\r\nUnholdCall: INFO;\r\nUnholdParticipant: INFO;\r\nWrapupTask: INFO;\r\n```\r\n\r\n### Twilio Event Hooks\r\n\r\nCurrently, the Twilio console only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with `twilioEventHooks` option, and use the following functions to send event data to a custom endpoint.\r\n\r\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is `application/x-www-form-urlencoded` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// unrelated init properties omitted from this example.\r\n\t// include in the twilioEventHooks array an entry for only the event hooks\r\n\t// the executing code is going to relay. For example, if the code is only\r\n\t// handling task router web hook events then you would only need to define\r\n\t// the taskRouter relay url here.\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\r\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\r\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\r\n\t},\r\n});\r\n\r\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\r\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\r\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\r\n```\r\n\r\nExample Azure Function hook for debugger events:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\nmodule.exports = async function(context, req) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\r\n\t\t},\r\n\t});\r\n\r\n\t// log to console\r\n\tcleLogger.info(\"Received Twilio debugger event\");\r\n\r\n\t// send event data to a configured url\r\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\r\n};\r\n```\r\n\r\n**NOTE WHEN USING THIS FUNCTIONALITY IN TWILIO FUNCTIONS:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to `x-www-form-urlencoded` format.\r\n\r\nExample Twilio function task router relay using the `form-urlencoded` npm package to convert the data to `x-www-form-urlencoded` format:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\nconst formurlencoded = require(\"form-urlencoded\").default;\r\n\r\nexports.handler = async function(context, event, callback) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\r\n\t\t},\r\n\t});\r\n\r\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\r\n\r\n\tcallback();\r\n};\r\n```\r\n\r\n### Version\r\n\r\nVersion will be automatically included in all logs.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.version; // 1.0.0\r\n```\r\n\r\n### Error To Object\r\n\r\nUtility function to convert an `Error` object to a plain JS object, with all properties preserved. When serialized, `Error` objects will omit custom properties from the output which makes debugging difficult. This utility will traverse all properties in the `Error` object, and output a plain JS object with all properties including the stack trace. See an example below.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\nconst err = new Error(\"error message\");\r\nconsole.log(err); // \"Error: error message\\n    at Object.<anonymous> …\"\r\n\r\nconst errObject = cleLogger.errorToObject(err);\r\nconsole.log(errObjct); // {name: 'Error', message: 'error message', stack: 'Error: error message\\n    at Object.<anonymous> …'}\r\n```\r\n\r\nExample usage when logging errors:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ntry {\r\n\t// some code that can throw an error\r\n} catch (error) {\r\n\tcleLogger.error(\"My error message\", cleLogger.errorToObject(error));\r\n}\r\n```\r\n\r\n## Log Object\r\n\r\nThe following is the type definition of the log object received by [log sinks](#log-sinks). It consists of properties provided in log messages, as well as some that are automatically inserted like the `eventDate`. You can use this information to customize your logs inside a custom log sink (e.g. filter out unwanted logs, modify some of the properties to include more data, or any kind of conditional logic based on the value of these properties). If using the built in `remoteLogger` log processor with your custom backend, you can expect an array of these objects to be sent to your log endpoint. For more info about implementing a custom logging endpoint see the [remote logging](#remote-logging) section.\r\n\r\n```js\r\n{\r\n\t// Account sid if provided during init\r\n\taccountSid: string;\r\n\r\n\t// Any additional log data to be included with the log\r\n\tadditionalData: unknown;\r\n\r\n\t// Component name, if provided\r\n\tcomponentName: string;\r\n\r\n\t// Generated or configured token\r\n\tcorrelationToken: string;\r\n\r\n\t// Current UTC datetime in ISO 8601 format\r\n\teventDate: string;\r\n\r\n\t// Log level\r\n\tlevel: string;\r\n\r\n\t// Log code, if provided\r\n\tlogCode: number;\r\n\r\n\t// Log message\r\n\tmessage: string;\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\tproperties: object;\r\n\r\n\t// BrowserFunction if the environment is the browser\r\n\t// CloudFunction if the environment is Node.js\r\n\tsource: string;\r\n\r\n\t// Indicates that the log contains PII data\r\n\tcontainsPII: boolean;\r\n\r\n\t// Logging library version\r\n\tloggerVersion: string;\r\n}\r\n```\r\n\r\n## Handling PII Data\r\n\r\nThe logger provides support for helping to protect log messages or data that need to include PII (Personally Identifiable Information). While the logger has several mechanisms that can be used to flag logs that contain PII and block PII from going to particular log sinks, it is still ultimately the developers responsibility to ensure PII is flagged or marked appropriately for the logger to know when it exists - the logger cannot automatically detect if PII is part of a log statement or data object. The general guideline is that developers should avoid including any PII data in log information whenever possible. In the instances where PII data must be included there are several mechanisms the logger supports to help indicate PII data and protect where it is sent.\r\n\r\nA `containsPII` parameter exists on log statement methods. This flag should be set to true if there is PII data, or any potential chance of it, in the log message, additional data or custom properties data. When this flag is set to true then it will only allow log sinks that have the `piiSafe` configuration set to true to receive and process the log message. `piiSafe` indicates on a log sink whether that log sink receiver is able to internally store PII data OR has logic to strip PII data that is tagged in the message or from pre-configured additional data fields before the log is written to any storage or output device. When sending data to any `remoteLogger` endpoint the log sink can be marked `piiSafe` only as long as data is appropriately tagged in messages and/or the names of custom properties that might have PII values in additional data are pre-configured AND the endpoint implementer has verified and ensured their receiving code will remove marked PII data immediately before sending logs to any output or storage media.\r\n\r\nTo tag any part of the message parameter in a log statement as containing PII within the actual log message use the `tagPII` utility. The `tagPII` utility will indicate to a remote backend that the text between the tags should be stripped or masked before sending logs to any output or storage media.\r\n\r\nExample tagged output:\r\n\r\n```js\r\nimport { tagPII } from \"@prftesp/cle-logger\";\r\n\r\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\r\n```\r\n\r\n## Advanced Configuration\r\n\r\n### Remote logging\r\n\r\nThe library provides a built in way to transmit logs to a remote http endpoint using the `remoteLogger` logs processor. The `remoteLogger` can be directed at a known listener such as CLE or you can create your own custom log listener. In addition, the log level in effect can be controlled remotely setting the `minLogLevelOverrideUrl` to an http address that will return the current log level to use instead of any defined default logLevel on each `logSink`. The `minLogLevelOverrideUrl` can point to a known central API such as CLE or you can create your own endpoint.\r\n\r\nThe following sections outline how to create your own endpoints for either receiving log messages from the `remoteLogger` log sink or providing the current log level override to it.\r\n\r\n**Remote log endpoint**\r\n\r\nTo implement your own remote logging endpoint, all you have to do is accept `POST` requests, with a JSON payload of an array of [log objects](#log-object), and return either `{ success: true }` or `{ success: false }`. The endpoint should always return a `200 OK` status even if your server code fails so that the CLE logger does not cause failures in the business logic in which it is embedded. A good use-case would be leveraging a serverless api platform to output logs to a queue.\r\n\r\nExample of an Azure Function that can receive requests from the `remoteLogger` log sink:\r\n\r\n```js\r\nmodule.exports = async function(context, req) {\r\n\tcontext.log(req.body); // body will have the log array\r\n\r\n\tcontext.res = {\r\n\t\tstatus: 200,\r\n\t\tbody: { success: true },\r\n\t};\r\n};\r\n```\r\n\r\n**Remote log level override**\r\n\r\nThe `minLogLevelOverrideUrl` configuration property is used to provide an endpoint that accepts `GET` requests and a `accountSid` query string parameter. `accountSid` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the `minLogLevel` configuration property. If null is returned, the `minLogLevel` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a `200 OK` status code and use the `success` property to indicate failure.\r\n\r\nExample success response:\r\n\r\n```json\r\n{\r\n\t\"success\": true,\r\n\t\"minLogLevel\": \"warn\"\r\n}\r\n```\r\n\r\nExample error response:\r\n\r\n```json\r\n{\r\n\t\"success\": false,\r\n\t\"minLogLevel\": null\r\n}\r\n```\r\n\r\n## Changelog\r\n\r\n### v2.1.0\r\n\r\n**New features**\r\n\r\n- Added [Error To Object](#error-to-object) method to assist users in converting error object to plain objects.\r\n\r\n### v2.0.7\r\n\r\n**New features**\r\n\r\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the call sid during a call, and automatically include it in `customProperties` log property for all logs that happen during that call (including Flex Action logs).\r\n\r\n### v2.0.5\r\n\r\n**New features**\r\n\r\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the worker sid and automatically include it in `customProperties` log property.\r\n\r\n**Other updates**\r\n\r\n- Updated documentation based on user feedback.\r\n\r\n### v2.0.4\r\n\r\n**Bug fixes**\r\n\r\n- Fixed issue with logging to console in Twilio Functions. The `consoleLogger` will now output the log object as serialized json.\r\n\r\n### v2.0.0 [2019-12-20]\r\n\r\n**New features**\r\n\r\n- All logging output is now configured using the `logSinks` property. The library provides two commonly used loggers - `consoleLogger` and a `remoteLogger` that accepts the log url as a parameter. [Configuration](#configuration)\r\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\r\n- Log functions have a new optional `containsPII` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\r\n- A new helper function `tagPII` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\r\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\r\n\r\n**Breaking changes**\r\n\r\n- `customLogSink` configuration renamed to `logSinks`\r\n- Removed `disableRemoteLogging` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\r\n- Removed `logToConsole` configuration. This is now configured as a log sink with a `consoleLogger` helper function.\r\n- Removed `logUrl` configuration. This is now configured as a log sink with a `remoteLogger` helper function.\r\n- Removed `minLogLevel` configuration. This is now configured per log sink.\r\n\r\n**Migration guide**\r\n\r\n```js\r\nimport cleLogger from \"@prftesp/cle-logger\";\r\n\r\n// v1.0.0 and older config\r\ncleLogger.init({\r\n\tdisableRemoteLogSinks: true, // no direct replacement\r\n\tlogToConsole: true, // replaced by the \"console logger\" sink\r\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\r\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\r\n\tcustomLogSinks: [\r\n\t\t{\r\n\t\t\tname: \"custom sink\",\r\n\t\t\tlog: logs => {\r\n\t\t\t\t/* your custom logging */\r\n\t\t\t},\r\n\t\t},\r\n\t],\r\n});\r\n\r\n// v2.0.0 config\r\ncleLogger.init({\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\tname: \"custom sink\",\r\n\t\t\tlog: logs => {\r\n\t\t\t\t/* your custom logging */\r\n\t\t\t},\r\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t\t// set to true to get feature parity with older version,\r\n\t\t\t// please consider your use case before setting\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"console logger\",\r\n\t\t\tlog: consoleLogger,\r\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t\t// set to true to get feature parity with older version,\r\n\t\t\t// please consider your use case before setting\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"remote logger\",\r\n\t\t\tlog: remoteLogger(\"http://log.output/api\"),\r\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t\t// set to true to get feature parity with older version,\r\n\t\t\t// please consider your use case before setting\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t],\r\n});\r\n```\r\n\r\n### v1.0.0 [2019-09-12]\r\n\r\n- Initial release\r\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.1.1-beta.0","_nodeVersion":"10.15.2","_npmVersion":"6.11.3","dist":{"integrity":"sha512-S6cUEv5tFGW9wcGep9njtuZ8f/3JSvJupnK+UkazwfQ6Eu4833Z/1ck3m1Q2gAt4REu/XnH+lbPcYOyYEfVL7w==","shasum":"d6b61b5c74c3582b35366fdd9f32044a1dcc135d","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.1.1-beta.0.tgz","fileCount":17,"unpackedSize":82052,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe38DhCRA9TVsSAnZWagAAsQ0P/jXQQVFNMZIDYMAlflbo\nCa4n4hlfkJtOUERtH3kNFljoXzX9PN336p2UwM0j+t7sCYJU7+izF8ROdkQh\nvrZI+l05WTZdGbFKDBkAGc90BIJTzfNmdwL6pUp9vsaENybpljtUkTQgMMfa\nO6OpFUm0QIAxmlfQ4HHbCEpGUCVNZTFr7466dJz4k33HLJnutNq5RiRuWoyR\nyOq7LAxxdyyOy6YF3D2j9DRWcRvr1Fe5W/+NGfpqEJQWAuQbx9MBih4rVOl0\nP/3EHy5H/MbygfFrbtiZy8zFZ8jJpWaihx4TovSgtBCjvKbrQRxevT234x1E\nReKMWTh2QeebmaEj/ksKuCBwIBb1d8pTV1yM+fr5u1XELtUDM8OUhDEzlCS+\nRVyEhdKIZGVT9vPtfDD+IAWHCLjJRZtTtpY+dTEbd1c4Qaho4sh6JR4HDVr2\nMpERnbVAGATh4JxCZ/tv6AV+IzNoGzC1PaUkXW1vNjJf7UlSfkKxAkyeQExI\no7xQBTSGxNgYDlqAliHOYsUahcc5k0SLvZfePshFz1xRJE5j2X3utPTPoBeJ\na3gCo8Zlaadr9UBb8aJjHAa4henpCmS8zn1BfbXSuO/PdrHkREZLs8bWVIsd\nDDZa5skbME+ZrQakC6/nNbz+aweJCHP10mSp3wUGseUoJ7jU41b6y1mgkVuv\n3b2J\r\n=/K7G\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDDxqXairyZiR1R0OxsoDv+b6zjVAioZrwcaTsKz+7TFgIhAJpdzh8hG+UZJet5e2T8d9XavpFtdxVbLVKrxnxuCVLi"}]},"maintainers":[{"email":"c.swartzentruber@perficient.com","name":"curtis.swartzentruber"},{"email":"darko.majkic@perficient.com","name":"darko.perficient"},{"email":"espnpm@perficient.com","name":"espnpm"},{"email":"mike.frank@perficient.com","name":"mikefrank"},{"email":"shelby.hagman@perficient.com","name":"shelby-prft"}],"_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.1.1-beta.0_1591722208442_0.5694396912565458"},"_hasShrinkwrap":false},"2.1.1-beta.1":{"name":"@prftesp/cle-logger","version":"2.1.1-beta.1","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build && npm run build:es5","build:es5":"rimraf dist-es5/* && tsc --build tsconfig.es5.json","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^25.1.4","@types/node":"^12.12.31","@typescript-eslint/eslint-plugin":"^2.25.0","@typescript-eslint/parser":"^2.25.0","eslint":"^6.8.0","eslint-config-prettier":"^6.10.1","eslint-plugin-prettier":"^3.1.2","jest":"^25.2.2","prettier":"^1.19.1","rimraf":"^3.0.2","ts-jest":"^25.2.1","typescript":"^3.8.3"},"dependencies":{"axios":"^0.19.2","serializerr":"^1.0.3"},"readme":"﻿CLE Logger\r\n==\r\n\r\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\r\n\r\n## Documentation\r\n\r\n- [Target Environment](#target-environment)\r\n- [Simple Example](#simple-example)\r\n- [Installation](#installation)\r\n- [Features](#features)\r\n- [Configuration](#configuration)\r\n- [API](#api)\r\n  - [Log sinks](#log-sinks)\r\n  - [Logging statements](#logging-statements)\r\n  - [Logging parameters](#logging-parameters)\r\n  - [Logging constants](#logging-constants)\r\n  - [Log codes](#log-codes)\r\n  - [Registering Twilio Actions](#registering-twilio-actions)\r\n  - [Twilio Event Hooks](#twilio-event-hooks)\r\n  - [Version](#version)\r\n  - [Error To Object](#error-to-object)\r\n- [Log Object](#log-object)\r\n- [Handling PII Data](#handling-pii-data)\r\n- [Advanced Configuration](#advanced-configuration)\r\n  - [Remote logging](#remote-logging)\r\n- [Changelog](#changelog)\r\n\r\n## Target Environment\r\n\r\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\r\n\r\n### ES5 Compatible Version\r\n\r\nIf you need an ES5 compatible version (for example you need IE11 support), use this package: https://www.npmjs.com/package/@prftesp/cle-logger-es5. **This package is not regularly released with the main library, and should be used only when absolutely necessary**. If you need to support IE11, don't forget to include the necessary polyfills, mainly for `Promise` support. To do this, you can include this script tag on the page, before you load this library `<script src=\"https://cdn.polyfill.io/v3/polyfill.min.js?features=es6\"></script>`, or visit `https://polyfill.io` for more options.\r\n\r\n**Please note** that the ES5 version is generated, and is sharing the same code and documentation as the main library. This is why the rest of the documentation doesn't mention the ES5 version in text or examples. The only difference is how you reference it (`@prftesp/cle-logger` vs `@prftesp/cle-logger-es5`). To install it use `npm install @prftesp/cle-logger-es5`, and to import it use `import { cleLogger } from \"@prftesp/cle-logger-es5\";`.\r\n\r\n## Simple Example\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\tname: \"console sink\",\r\n\t\t\tlog: cleLogger.consoleLogger,\r\n\t\t\tpiiSafe: true,\r\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\r\n\t\t},\r\n\t],\r\n});\r\n\r\n// will log message to console\r\ncleLogger.info(\"log message\");\r\n```\r\n\r\n## Installation\r\n\r\n```bash\r\n$ npm install @prftesp/cle-logger\r\n```\r\n\r\n## Features\r\n\r\n- Runs in Node.js and the browser\r\n- Remote logging\r\n- Multiple log sinks\r\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\r\n- Remote minimum log level overrides\r\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\r\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\r\n- Support for tagging logs with custom component names or correlation tokens\r\n- Send Twilio event hook data to a custom backend\r\n\r\n## Configuration\r\n\r\nTo configure the logger call the `init` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\r\n\r\n**Available options**\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// Twilio account sid, required for use with CLE\r\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\r\n\r\n\t// Will be associated with all logs,\r\n\t// if not provided, a unique uuid will be generated\r\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\r\n\t// An array of that will receive an array of log objects,\r\n\t// and can optionally return a promise. Please see the\r\n\t// \"Log Sinks\" section for more information.\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\t// name of the log sink\r\n\t\t\tname: \"exampleConsoleSink\",\r\n\t\t\t// log handler function\r\n\t\t\tlog: cleLogger.consoleLogger,\r\n\t\t\t// This flag defines if the log message is processed by this sink when the contains\r\n\t\t\t// PII parameter of the log statement is true. piiSafe of true indicates all log\r\n\t\t\t// messages are allowed (even when contains PII is true); piiSafe of false indicates\r\n\t\t\t// only log messages with contains PII set to false will be processed by this log sink.\r\n\t\t\t// Defaults to false\r\n\t\t\tpiiSafe: true,\r\n\t\t\t// log level for the sink, defaults to INFO\r\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\r\n\t\t},\r\n\t],\r\n\r\n\t// A way to suppress error messages if set to false, defaults to true\r\n\tlogErrorsToConsole: true,\r\n\r\n\t// If logging Twilio Flex Actions, provide a list of actions\r\n\t// to be excluded from logs\r\n\texcludedFlexActionNames: [\"SendTyping\"],\r\n\r\n\t// A custom endpoint to fetch log level overrides\r\n\t// NOTE: the provided url should always end with 'accountSid=',\r\n\t// the library will append the accountSid\r\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\r\n\r\n\t// An instance of ('@twilio/flex-ui').Manager.\r\n\t// If provided, it will be used to get the worker sid and call sid (only during a call), and\r\n\t// automatically include it in 'customProperties' log property and with every log\r\n\ttwilioFlexManager: manager,\r\n\r\n\t// To send data from Twilio event hooks, to a custom backend,\r\n\t// configure one or more endpoints for each event type\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\r\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\r\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\r\n\t},\r\n});\r\n```\r\n\r\n## API\r\n\r\nThe logger can be imported as an object in CommonJs or ES6 syntax:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n// or\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n```\r\n\r\nAlternatively, all top level members of the `cleLogger` are available as named exports and can be imported separately:\r\n\r\n```js\r\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tlogSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\r\n\r\n// vs\r\n\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tcleLogger.logSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\r\n```\r\n\r\n### Log sinks\r\n\r\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the `logSinks` config. The logSinks configuration defines an array of objects with each object defining the log sink activity. All provided log sinks will be executed in parallel. A log sink configuration must include the following properties:\r\n\r\n- `name`: this is an identifiable name for the logger and must be unique per log sink defined\r\n- `log`: this indicates the logic to process on each batch of logs processed by the sink. There are several built in processing functions that can be used or you can insert any code inline with this definition. The built in processing functions are `consoleLogger` which will send the log statements to the attached console or `remoteLogger` which will send the log statements to the specified url provided as a definition on the parameter (i.e. `remoteLogger(\"https://mylogger.url\"`). For a custom log processing implementation, the logic must be async operations and must return a Promise object. The custom log method will receive an array of log objects for you to handle. You can find out more about the log object type in the [Log Object](#log-object) section. Here is an example of a custom log function:\r\n\r\n```js\r\nlogs => {\r\n\t// log each received log to the console separately\r\n\tlogs.forEach(log => console.log(log));\r\n\treturn Promise.resolve();\r\n};\r\n```\r\n\r\n- `logLevel`: defines the minimum default log level that will be processed by this log sink. Log levels lower than the minimum level will not be processed by this log sink. The minimum default log level can be overridden at run time by the value returned from the overall `minLogLevelOverrideUrl` that is set up in the library `init`.\r\n- `piiSafe`: indicates if log messages with a `containsPII` parameter value of true should be processed by the log sink. If `containsPII` parameter value on the log message is true and piiSafe is false then the log message will not be processed by this log sink, if piiSafe is true then it will. If the `containsPII` parameter value is false on the log message than it is always processed by the log sink regardless of the piiSafe value.\r\n\r\nCustom log sink configuration example. In this example, the custom code executed for each log statement is to append the log into a file. Note how a Promise is returned as required:\r\n\r\n```js\r\n{\r\n\t// name of the log sink\r\n\tname: \"exampleConsoleSink\",\r\n\t// log handler function - this is where you put your custom handling code and\r\n\t// remember to always return a promise. If your operation is not asynchronous,\r\n\t// you can do this by returning Promise.resolve().\r\n\tlog: logs => {\r\n\t\tfs.appendFile(\r\n\t\t\t\"./log.txt\",\r\n\t\t\t`${JSON.stringify(logs)}`,\r\n\t\t\tconsole.log\r\n\t\t);\r\n\t\treturn Promise.resolve();\r\n\t},\r\n\t// should this log sink accept PII data, defaults to false\r\n\tpiiSafe: true,\r\n\t// log level for the sink, defaults to INFO\r\n\tlogLevel: cleLogger.logLevels.VERBOSE\r\n}\r\n```\r\n\r\nExample log sink configuration that uses a custom coded sink and both standard console and remote logging sinks:\r\n\r\n```js\r\nconst fs = require(\"fs\");\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\ncleLogger.init({\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\tname: \"fileSink\",\r\n\t\t\tlog: logs => {\r\n\t\t\t\tfs.appendFile(\"./log.txt\", `${JSON.stringify(logs)}`, console.log);\r\n\t\t\t\treturn Promise.resolve();\r\n\t\t\t},\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"remoteSink\",\r\n\t\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\r\n\t\t\tlogLevel: cleLogger.logLevels.WARN,\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"consoleSink\",\r\n\t\t\tlog: cleLogger.consoleLogger,\r\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\r\n\t\t},\r\n\t],\r\n});\r\n```\r\n\r\n### Logging statements\r\n\r\nTo create logs, you can use one of the logging function available. They all take the same arguments, described below, and log at a corresponding log level. For example, if you use the `cleLogger.verbose` logging function, and your log sink is configured with `logLevel: cleLogger.logLevels.VERBOSE`, that log sink will receive the log. If your log sink is configured with a higher log level, for example `logLevel: cleLogger.logLevels.INFO`, then it would not receive the verbose log.\r\n\r\nHere are the available logging functions:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(...);\r\ncleLogger.info(...);\r\ncleLogger.warn(...);\r\ncleLogger.error(...);\r\ncleLogger.critical(...);\r\n```\r\n\r\nThe functions are all async, return `Promise<void>`, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\r\n\r\n### Logging parameters\r\n\r\nAll logging functions take the same parameters:\r\n\r\n- `message`: the only required property, no logs will be generated without it\r\n  - type: string\r\n  - required: yes\r\n- `additionalData`: any additional log data you would like to include with the log, can be any data type\r\n  - type: any\r\n  - required: no\r\n  - default value: null\r\n- `customProperties`: a single level deep object of important properties you would like to distinguish from general log data, like various sids CLE uses it to index these properties in Application Insights. For CLE, try to use keys found in `cleLogger.customPropertyKeys`\r\n  - type: object\r\n  - required: no\r\n  - default value: {}\r\n- `componentName`: another useful way to 'tag' your logs for easier searching. Can be any string, but for CLE, try to use available options in `cleLogger.componentNames`\r\n  - type: string\r\n  - required: no\r\n  - default value: \"\"\r\n- `logCode`: log code, can be any number used to categorize logs. For defined ranges see the [log codes](#log-codes) section.\r\n  - type: number\r\n  - required: no\r\n  - default value: 0\r\n- `correlationToken`: correlation token to be associated with this log only\r\n  - type: string\r\n  - required: no\r\n  - default value: token provided in `init`, or a generated token if one wasn't provided\r\n- `containsPII`: does the log contain PII data, defaults to false. If set to true, the log will be passed in only to those log sinks that have `piiSafe` set to true. For more info see the [handling PII data](#handling-pii-data) section.\r\n  - type: boolean\r\n  - required: no\r\n  - default value: false\r\n\r\nExample verbose logging statement:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(\r\n\t\"Log message\", // message\r\n\t{ data: \"test\" }, // additionalData\r\n\t{ [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" }, // customProperties\r\n\tcleLogger.componentNames.SESSION, // componentName\r\n\t70123, // logCode\r\n\t\"my-token\", // correlationToken\r\n\ttrue, // containsPII\r\n);\r\n```\r\n\r\n### Logging constants\r\n\r\n**Logging levels**\r\n\r\nLogging level constants should be used when configuring the log level of a [log sink](#log-sinks).\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.logLevels.VERBOSE; // verbose\r\ncleLogger.logLevels.INFO; // info\r\ncleLogger.logLevels.WARN; // warn\r\ncleLogger.logLevels.ERROR; // error\r\ncleLogger.logLevels.CRITICAL; // critical\r\n```\r\n\r\n**Component names**\r\n\r\nDefines a set of commonly used components that should be used for the `componentName` parameter value of a log statement if you are logging from one of these common components in a Twilio implementation.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.componentNames.SESSION;\r\ncleLogger.componentNames.DIALER;\r\ncleLogger.componentNames.DASHBOARD;\r\ncleLogger.componentNames.AFTERCALL;\r\ncleLogger.componentNames.SUPERVISOR;\r\n```\r\n\r\n**Custom property keys**\r\n\r\nDefines a set of commonly used key that should be used when defining the custom properties object that is supplied in the `customProperties` parameter of any logging statement.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.customPropertyKeys.CHANNEL_SID;\r\ncleLogger.customPropertyKeys.TASK_SID;\r\ncleLogger.customPropertyKeys.CALL_SID;\r\ncleLogger.customPropertyKeys.WORKFLOW_SID;\r\ncleLogger.customPropertyKeys.WORKER_SID;\r\n```\r\n\r\n### Log codes\r\n\r\nIt can be helpful when investigating issues to only view logs related to specific areas of a system. Use Log Codes to give a category definition for what area of the contact center the log activity occurred within. Use numbers within the following pre-defined log code ranges or define custom log codes by using your own numbers higher than 300,000.\r\n\r\n- 100 000 - 109 999 => CALL_STATUS\r\n- 110 000 - 119 999 => IVR\r\n- 120 000 - 129 999 => ROUTING\r\n- 130 000 - 139 999 => AGENT_ACTION\r\n- 140 000 - 149 999 => TRANSFER\r\n- 150 000 - 159 999 => RM_INTEGRATION\r\n- 160 000 - 169 999 => WFM_INTEGRATION\r\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\r\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\r\n\r\n### Registering Twilio Actions\r\n\r\nIt is important to log information from the Twilio Flex UI for all implementations even when there is little customization happening. Many developers find it useful to generate log activity when some, or any, of the Twilio Actions occur within the Twilio Flex UI. Twilio Actions are events that arise from the Twilio Flex UI base implementation and indicate when certain activity is occurring - such as an agent hanging up a call. The CLE Logger library can be setup to automatically register all, or selected, Twilio Actions such that right after any code tied to the Action event has been executed a log statement is written indicating the Action occurred. This log statement will also automatically include the action event payload. To enable this logging capability, use the following approach to register the Twilio Actions. This only needs to be performed once during the initialization of the Twilio Flex UI page on the client.\r\n\r\nExample in a Twilio Flex UI plugin:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\n/**\r\n * @param flex { typeof import('@twilio/flex-ui') }\r\n * @param manager { import('@twilio/flex-ui').Manager }\r\n */\r\ninit(flex, manager) {\r\n\tcleLogger.init({\r\n\t\taccountSid: \"your-account-sid\"\r\n\t});\r\n\r\n\tcleLogger.registerTwilioActions(flex);\r\n}\r\n```\r\n\r\n**Note**: A Twilio account sid must be provided during `init` with the `accountSid` parameter, otherwise the actions won't be logged.\r\n\r\nThe logger will loop through all actions found under `flex.Actions.actions`, and attach `after` event handlers to log the action name and payload. Any private properties found in the payload (starting with '\\_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Not all Action events need to be logged and by using the `excludedFlexActionNames` property in the logger configuration the Action events not desired can be ignored. Any actions names configured in `excludedFlexActionNames` will not be included in the logs.\r\n\r\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\r\n\r\n**Twilio Actions log levels**\r\n\r\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of `INFO`.\r\n\r\n```js\r\nAcceptTask: INFO;\r\nCancelTransfer: INFO;\r\nCompleteTask: INFO;\r\nHangupCall: INFO;\r\nHideDirectory: VERBOSE;\r\nHistoryGo: VERBOSE;\r\nHistoryGoBack: VERBOSE;\r\nHistoryGoForward: VERBOSE;\r\nHistoryPush: VERBOSE;\r\nHistoryReplace: VERBOSE;\r\nHoldCall: INFO;\r\nHoldParticipant: INFO;\r\nKickParticipant: INFO;\r\nLogout: INFO;\r\nMonitorCall: INFO;\r\nNavigateToView: INFO;\r\nRejectTask: INFO;\r\nSelectTask: INFO;\r\nSelectTaskInSupervisor: INFO;\r\nSelectWorkerInSupervisor: INFO;\r\nSendMessage: INFO;\r\nSendTyping: VERBOSE;\r\nSetActivity: INFO;\r\nSetInputText: VERBOSE;\r\nShowDirectory: VERBOSE;\r\nStopMonitoringCall: INFO;\r\nToggleMute: INFO;\r\nToggleSidebar: VERBOSE;\r\nTransferTask: INFO;\r\nUnholdCall: INFO;\r\nUnholdParticipant: INFO;\r\nWrapupTask: INFO;\r\n```\r\n\r\n### Twilio Event Hooks\r\n\r\nCurrently, the Twilio console only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with `twilioEventHooks` option, and use the following functions to send event data to a custom endpoint.\r\n\r\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is `application/x-www-form-urlencoded` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// unrelated init properties omitted from this example.\r\n\t// include in the twilioEventHooks array an entry for only the event hooks\r\n\t// the executing code is going to relay. For example, if the code is only\r\n\t// handling task router web hook events then you would only need to define\r\n\t// the taskRouter relay url here.\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\r\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\r\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\r\n\t},\r\n});\r\n\r\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\r\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\r\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\r\n```\r\n\r\nExample Azure Function hook for debugger events:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\nmodule.exports = async function(context, req) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\r\n\t\t},\r\n\t});\r\n\r\n\t// log to console\r\n\tcleLogger.info(\"Received Twilio debugger event\");\r\n\r\n\t// send event data to a configured url\r\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\r\n};\r\n```\r\n\r\n**NOTE WHEN USING THIS FUNCTIONALITY IN TWILIO FUNCTIONS:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to `x-www-form-urlencoded` format.\r\n\r\nExample Twilio function task router relay using the `form-urlencoded` npm package to convert the data to `x-www-form-urlencoded` format:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\nconst formurlencoded = require(\"form-urlencoded\").default;\r\n\r\nexports.handler = async function(context, event, callback) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\r\n\t\t},\r\n\t});\r\n\r\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\r\n\r\n\tcallback();\r\n};\r\n```\r\n\r\n### Version\r\n\r\nVersion will be automatically included in all logs.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.version; // 1.0.0\r\n```\r\n\r\n### Error To Object\r\n\r\nUtility function to convert an `Error` object to a plain JS object, with all properties preserved. When serialized, `Error` objects will omit custom properties from the output which makes debugging difficult. This utility will traverse all properties in the `Error` object, and output a plain JS object with all properties including the stack trace. See an example below.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\nconst err = new Error(\"error message\");\r\nconsole.log(err); // \"Error: error message\\n    at Object.<anonymous> …\"\r\n\r\nconst errObject = cleLogger.errorToObject(err);\r\nconsole.log(errObjct); // {name: 'Error', message: 'error message', stack: 'Error: error message\\n    at Object.<anonymous> …'}\r\n```\r\n\r\nExample usage when logging errors:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ntry {\r\n\t// some code that can throw an error\r\n} catch (error) {\r\n\tcleLogger.error(\"My error message\", cleLogger.errorToObject(error));\r\n}\r\n```\r\n\r\n## Log Object\r\n\r\nThe following is the type definition of the log object received by [log sinks](#log-sinks). It consists of properties provided in log messages, as well as some that are automatically inserted like the `eventDate`. You can use this information to customize your logs inside a custom log sink (e.g. filter out unwanted logs, modify some of the properties to include more data, or any kind of conditional logic based on the value of these properties). If using the built in `remoteLogger` log processor with your custom backend, you can expect an array of these objects to be sent to your log endpoint. For more info about implementing a custom logging endpoint see the [remote logging](#remote-logging) section.\r\n\r\n```js\r\n{\r\n\t// Account sid if provided during init\r\n\taccountSid: string;\r\n\r\n\t// Any additional log data to be included with the log\r\n\tadditionalData: unknown;\r\n\r\n\t// Component name, if provided\r\n\tcomponentName: string;\r\n\r\n\t// Generated or configured token\r\n\tcorrelationToken: string;\r\n\r\n\t// Current UTC datetime in ISO 8601 format\r\n\teventDate: string;\r\n\r\n\t// Log level\r\n\tlevel: string;\r\n\r\n\t// Log code, if provided\r\n\tlogCode: number;\r\n\r\n\t// Log message\r\n\tmessage: string;\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\tproperties: object;\r\n\r\n\t// BrowserFunction if the environment is the browser\r\n\t// CloudFunction if the environment is Node.js\r\n\tsource: string;\r\n\r\n\t// Indicates that the log contains PII data\r\n\tcontainsPII: boolean;\r\n\r\n\t// Logging library version\r\n\tloggerVersion: string;\r\n}\r\n```\r\n\r\n## Handling PII Data\r\n\r\nThe logger provides support for helping to protect log messages or data that need to include PII (Personally Identifiable Information). While the logger has several mechanisms that can be used to flag logs that contain PII and block PII from going to particular log sinks, it is still ultimately the developers responsibility to ensure PII is flagged or marked appropriately for the logger to know when it exists - the logger cannot automatically detect if PII is part of a log statement or data object. The general guideline is that developers should avoid including any PII data in log information whenever possible. In the instances where PII data must be included there are several mechanisms the logger supports to help indicate PII data and protect where it is sent.\r\n\r\nA `containsPII` parameter exists on log statement methods. This flag should be set to true if there is PII data, or any potential chance of it, in the log message, additional data or custom properties data. When this flag is set to true then it will only allow log sinks that have the `piiSafe` configuration set to true to receive and process the log message. `piiSafe` indicates on a log sink whether that log sink receiver is able to internally store PII data OR has logic to strip PII data that is tagged in the message or from pre-configured additional data fields before the log is written to any storage or output device. When sending data to any `remoteLogger` endpoint the log sink can be marked `piiSafe` only as long as data is appropriately tagged in messages and/or the names of custom properties that might have PII values in additional data are pre-configured AND the endpoint implementer has verified and ensured their receiving code will remove marked PII data immediately before sending logs to any output or storage media.\r\n\r\nTo tag any part of the message parameter in a log statement as containing PII within the actual log message use the `tagPII` utility. The `tagPII` utility will indicate to a remote backend that the text between the tags should be stripped or masked before sending logs to any output or storage media.\r\n\r\nExample tagged output:\r\n\r\n```js\r\nimport { tagPII } from \"@prftesp/cle-logger\";\r\n\r\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\r\n```\r\n\r\n## Advanced Configuration\r\n\r\n### Remote logging\r\n\r\nThe library provides a built in way to transmit logs to a remote http endpoint using the `remoteLogger` logs processor. The `remoteLogger` can be directed at a known listener such as CLE or you can create your own custom log listener. In addition, the log level in effect can be controlled remotely setting the `minLogLevelOverrideUrl` to an http address that will return the current log level to use instead of any defined default logLevel on each `logSink`. The `minLogLevelOverrideUrl` can point to a known central API such as CLE or you can create your own endpoint.\r\n\r\nThe following sections outline how to create your own endpoints for either receiving log messages from the `remoteLogger` log sink or providing the current log level override to it.\r\n\r\n**Remote log endpoint**\r\n\r\nTo implement your own remote logging endpoint, all you have to do is accept `POST` requests, with a JSON payload of an array of [log objects](#log-object), and return either `{ success: true }` or `{ success: false }`. The endpoint should always return a `200 OK` status even if your server code fails so that the CLE logger does not cause failures in the business logic in which it is embedded. A good use-case would be leveraging a serverless api platform to output logs to a queue.\r\n\r\nExample of an Azure Function that can receive requests from the `remoteLogger` log sink:\r\n\r\n```js\r\nmodule.exports = async function(context, req) {\r\n\tcontext.log(req.body); // body will have the log array\r\n\r\n\tcontext.res = {\r\n\t\tstatus: 200,\r\n\t\tbody: { success: true },\r\n\t};\r\n};\r\n```\r\n\r\n**Remote log level override**\r\n\r\nThe `minLogLevelOverrideUrl` configuration property is used to provide an endpoint that accepts `GET` requests and a `accountSid` query string parameter. `accountSid` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the `minLogLevel` configuration property. If null is returned, the `minLogLevel` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a `200 OK` status code and use the `success` property to indicate failure.\r\n\r\nExample success response:\r\n\r\n```json\r\n{\r\n\t\"success\": true,\r\n\t\"minLogLevel\": \"warn\"\r\n}\r\n```\r\n\r\nExample error response:\r\n\r\n```json\r\n{\r\n\t\"success\": false,\r\n\t\"minLogLevel\": null\r\n}\r\n```\r\n\r\n## Changelog\r\n\r\n### v2.1.0\r\n\r\n**New features**\r\n\r\n- Added [Error To Object](#error-to-object) method to assist users in converting error object to plain objects.\r\n\r\n### v2.0.7\r\n\r\n**New features**\r\n\r\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the call sid during a call, and automatically include it in `customProperties` log property for all logs that happen during that call (including Flex Action logs).\r\n\r\n### v2.0.5\r\n\r\n**New features**\r\n\r\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the worker sid and automatically include it in `customProperties` log property.\r\n\r\n**Other updates**\r\n\r\n- Updated documentation based on user feedback.\r\n\r\n### v2.0.4\r\n\r\n**Bug fixes**\r\n\r\n- Fixed issue with logging to console in Twilio Functions. The `consoleLogger` will now output the log object as serialized json.\r\n\r\n### v2.0.0 [2019-12-20]\r\n\r\n**New features**\r\n\r\n- All logging output is now configured using the `logSinks` property. The library provides two commonly used loggers - `consoleLogger` and a `remoteLogger` that accepts the log url as a parameter. [Configuration](#configuration)\r\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\r\n- Log functions have a new optional `containsPII` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\r\n- A new helper function `tagPII` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\r\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\r\n\r\n**Breaking changes**\r\n\r\n- `customLogSink` configuration renamed to `logSinks`\r\n- Removed `disableRemoteLogging` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\r\n- Removed `logToConsole` configuration. This is now configured as a log sink with a `consoleLogger` helper function.\r\n- Removed `logUrl` configuration. This is now configured as a log sink with a `remoteLogger` helper function.\r\n- Removed `minLogLevel` configuration. This is now configured per log sink.\r\n\r\n**Migration guide**\r\n\r\n```js\r\nimport cleLogger from \"@prftesp/cle-logger\";\r\n\r\n// v1.0.0 and older config\r\ncleLogger.init({\r\n\tdisableRemoteLogSinks: true, // no direct replacement\r\n\tlogToConsole: true, // replaced by the \"console logger\" sink\r\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\r\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\r\n\tcustomLogSinks: [\r\n\t\t{\r\n\t\t\tname: \"custom sink\",\r\n\t\t\tlog: logs => {\r\n\t\t\t\t/* your custom logging */\r\n\t\t\t},\r\n\t\t},\r\n\t],\r\n});\r\n\r\n// v2.0.0 config\r\ncleLogger.init({\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\tname: \"custom sink\",\r\n\t\t\tlog: logs => {\r\n\t\t\t\t/* your custom logging */\r\n\t\t\t},\r\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t\t// set to true to get feature parity with older version,\r\n\t\t\t// please consider your use case before setting\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"console logger\",\r\n\t\t\tlog: consoleLogger,\r\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t\t// set to true to get feature parity with older version,\r\n\t\t\t// please consider your use case before setting\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"remote logger\",\r\n\t\t\tlog: remoteLogger(\"http://log.output/api\"),\r\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t\t// set to true to get feature parity with older version,\r\n\t\t\t// please consider your use case before setting\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t],\r\n});\r\n```\r\n\r\n### v1.0.0 [2019-09-12]\r\n\r\n- Initial release\r\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.1.1-beta.1","_nodeVersion":"10.15.2","_npmVersion":"6.11.3","dist":{"integrity":"sha512-Xttg2H/88S2nuVbLm4OzY8vxC3nV4EOpnFw+5LywuZCItISE8F+pCjeJyqU2q4jzkFDxejyXbP/dP5Vtewjv6A==","shasum":"70aae732a17189c85898e88745ab18ee4895374f","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.1.1-beta.1.tgz","fileCount":17,"unpackedSize":82093,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe38GBCRA9TVsSAnZWagAAdL0P/1+oH9wiIMPK1Cc0HTnF\nsI8M9azD+x5vKw4AomA/G+hdaLxcJODaC83LQ3doDCC1wRrChwpNEZelLhFl\n1fDa2jDtOASO3YEUEg69s4/EsKGvYJZeb8zLl239LQNgcIi7spMOJIwJi0d4\nMGM4CLwEHlRd0d8xlCRPyjlHdYQWjXv6Raw2hRwgV0s7ZQEHaYxk42Cm89lG\nmRvwCWpOtpxczLwX3d2rCW6EBjjAyDHOANNkZrasERNF1q7CSC6BfrfQXQz/\naSlgsKEPWXwxLgh85sWc8KHS6BVH/T72/VRDdP8vmRd37U1O4Io6YteLOXqk\nyRanvq3ie68piZ+TRR9P5zh2jd0p5yL+kbnHyyK79vuQH9vPEIWkYkQwVBcu\nJAkfRAc7nW/eFsQ1hxFXMpD8Q/ebQHwKPxyGBGbyfHGpmegbIvbU4H3aoTEo\n7bylGeRStOVXy/LQ7gMaspFlaSfbRket9iWY6qWSrgWhQylqzq8wf0+vBSMv\nmoAPC0aTJ/UkrSMZVenh+dEA1wLTVT0E1Jdx7El0Zb8SRSgL1S7T5Cpxy7Tm\n9pIR1SiHHBH/011fmAZnIzf7iLJgsA/R/R7mLpmdeFlYtH+TPy+X30UDjTAx\n21Zfh+V9FzaQZyXK+dYPsfxPOdTDurBTuxCJPRDucFEsSuqTX66QZsWZ5Mwy\npM6b\r\n=vSqv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD+FIMnQimPBmaghqy7PbhNUwjG+c8Lo1qsvoB79mj62gIgYYu93swLQfgYV+W2WCVpyyGorJ6qCVcZ49ldVJpy3jk="}]},"maintainers":[{"email":"c.swartzentruber@perficient.com","name":"curtis.swartzentruber"},{"email":"darko.majkic@perficient.com","name":"darko.perficient"},{"email":"espnpm@perficient.com","name":"espnpm"},{"email":"mike.frank@perficient.com","name":"mikefrank"},{"email":"shelby.hagman@perficient.com","name":"shelby-prft"}],"_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.1.1-beta.1_1591722369137_0.2897175456034018"},"_hasShrinkwrap":false},"2.1.1-beta.2":{"name":"@prftesp/cle-logger","version":"2.1.1-beta.2","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build && npm run build:es5","build:es5":"rimraf dist-es5/* && tsc --build tsconfig.es5.json","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta","local:beta":"npm run build && npm run publish-lib-beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^25.1.4","@types/node":"^12.12.31","@typescript-eslint/eslint-plugin":"^2.25.0","@typescript-eslint/parser":"^2.25.0","eslint":"^6.8.0","eslint-config-prettier":"^6.10.1","eslint-plugin-prettier":"^3.1.2","jest":"^25.2.2","prettier":"^1.19.1","rimraf":"^3.0.2","ts-jest":"^25.2.1","typescript":"^3.8.3"},"dependencies":{"axios":"^0.19.2","serializerr":"^1.0.3"},"readme":"﻿CLE Logger\r\n==\r\n\r\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\r\n\r\n## Documentation\r\n\r\n- [Target Environment](#target-environment)\r\n- [Simple Example](#simple-example)\r\n- [Installation](#installation)\r\n- [Features](#features)\r\n- [Configuration](#configuration)\r\n- [API](#api)\r\n  - [Log sinks](#log-sinks)\r\n  - [Logging statements](#logging-statements)\r\n  - [Logging parameters](#logging-parameters)\r\n  - [Logging constants](#logging-constants)\r\n  - [Log codes](#log-codes)\r\n  - [Registering Twilio Actions](#registering-twilio-actions)\r\n  - [Twilio Event Hooks](#twilio-event-hooks)\r\n  - [Version](#version)\r\n  - [Error To Object](#error-to-object)\r\n- [Log Object](#log-object)\r\n- [Handling PII Data](#handling-pii-data)\r\n- [Advanced Configuration](#advanced-configuration)\r\n  - [Remote logging](#remote-logging)\r\n- [Changelog](#changelog)\r\n\r\n## Target Environment\r\n\r\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\r\n\r\n### ES5 Compatible Version\r\n\r\nIf you need an ES5 compatible version (for example you need IE11 support), use this package: https://www.npmjs.com/package/@prftesp/cle-logger-es5. **This package is not regularly released with the main library, and should be used only when absolutely necessary**. If you need to support IE11, don't forget to include the necessary polyfills, mainly for `Promise` support. To do this, you can include this script tag on the page, before you load this library `<script src=\"https://cdn.polyfill.io/v3/polyfill.min.js?features=es6\"></script>`, or visit `https://polyfill.io` for more options.\r\n\r\n**Please note** that the ES5 version is generated, and is sharing the same code and documentation as the main library. This is why the rest of the documentation doesn't mention the ES5 version in text or examples. The only difference is how you reference it (`@prftesp/cle-logger` vs `@prftesp/cle-logger-es5`). To install it use `npm install @prftesp/cle-logger-es5`, and to import it use `import { cleLogger } from \"@prftesp/cle-logger-es5\";`.\r\n\r\n## Simple Example\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\tname: \"console sink\",\r\n\t\t\tlog: cleLogger.consoleLogger,\r\n\t\t\tpiiSafe: true,\r\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\r\n\t\t},\r\n\t],\r\n});\r\n\r\n// will log message to console\r\ncleLogger.info(\"log message\");\r\n```\r\n\r\n## Installation\r\n\r\n```bash\r\n$ npm install @prftesp/cle-logger\r\n```\r\n\r\n## Features\r\n\r\n- Runs in Node.js and the browser\r\n- Remote logging\r\n- Multiple log sinks\r\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\r\n- Remote minimum log level overrides\r\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\r\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\r\n- Support for tagging logs with custom component names or correlation tokens\r\n- Send Twilio event hook data to a custom backend\r\n\r\n## Configuration\r\n\r\nTo configure the logger call the `init` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\r\n\r\n**Available options**\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// Twilio account sid, required for use with CLE\r\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\r\n\r\n\t// Will be associated with all logs,\r\n\t// if not provided, a unique uuid will be generated\r\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\r\n\t// An array of that will receive an array of log objects,\r\n\t// and can optionally return a promise. Please see the\r\n\t// \"Log Sinks\" section for more information.\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\t// name of the log sink\r\n\t\t\tname: \"exampleConsoleSink\",\r\n\t\t\t// log handler function\r\n\t\t\tlog: cleLogger.consoleLogger,\r\n\t\t\t// This flag defines if the log message is processed by this sink when the contains\r\n\t\t\t// PII parameter of the log statement is true. piiSafe of true indicates all log\r\n\t\t\t// messages are allowed (even when contains PII is true); piiSafe of false indicates\r\n\t\t\t// only log messages with contains PII set to false will be processed by this log sink.\r\n\t\t\t// Defaults to false\r\n\t\t\tpiiSafe: true,\r\n\t\t\t// log level for the sink, defaults to INFO\r\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\r\n\t\t},\r\n\t],\r\n\r\n\t// A way to suppress error messages if set to false, defaults to true\r\n\tlogErrorsToConsole: true,\r\n\r\n\t// If logging Twilio Flex Actions, provide a list of actions\r\n\t// to be excluded from logs\r\n\texcludedFlexActionNames: [\"SendTyping\"],\r\n\r\n\t// A custom endpoint to fetch log level overrides\r\n\t// NOTE: the provided url should always end with 'accountSid=',\r\n\t// the library will append the accountSid\r\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\r\n\r\n\t// An instance of ('@twilio/flex-ui').Manager.\r\n\t// If provided, it will be used to get the worker sid and call sid (only during a call), and\r\n\t// automatically include it in 'customProperties' log property and with every log\r\n\ttwilioFlexManager: manager,\r\n\r\n\t// To send data from Twilio event hooks, to a custom backend,\r\n\t// configure one or more endpoints for each event type\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\r\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\r\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\r\n\t},\r\n});\r\n```\r\n\r\n## API\r\n\r\nThe logger can be imported as an object in CommonJs or ES6 syntax:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n// or\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n```\r\n\r\nAlternatively, all top level members of the `cleLogger` are available as named exports and can be imported separately:\r\n\r\n```js\r\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tlogSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\r\n\r\n// vs\r\n\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tcleLogger.logSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\r\n```\r\n\r\n### Log sinks\r\n\r\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the `logSinks` config. The logSinks configuration defines an array of objects with each object defining the log sink activity. All provided log sinks will be executed in parallel. A log sink configuration must include the following properties:\r\n\r\n- `name`: this is an identifiable name for the logger and must be unique per log sink defined\r\n- `log`: this indicates the logic to process on each batch of logs processed by the sink. There are several built in processing functions that can be used or you can insert any code inline with this definition. The built in processing functions are `consoleLogger` which will send the log statements to the attached console or `remoteLogger` which will send the log statements to the specified url provided as a definition on the parameter (i.e. `remoteLogger(\"https://mylogger.url\"`). For a custom log processing implementation, the logic must be async operations and must return a Promise object. The custom log method will receive an array of log objects for you to handle. You can find out more about the log object type in the [Log Object](#log-object) section. Here is an example of a custom log function:\r\n\r\n```js\r\nlogs => {\r\n\t// log each received log to the console separately\r\n\tlogs.forEach(log => console.log(log));\r\n\treturn Promise.resolve();\r\n};\r\n```\r\n\r\n- `logLevel`: defines the minimum default log level that will be processed by this log sink. Log levels lower than the minimum level will not be processed by this log sink. The minimum default log level can be overridden at run time by the value returned from the overall `minLogLevelOverrideUrl` that is set up in the library `init`.\r\n- `piiSafe`: indicates if log messages with a `containsPII` parameter value of true should be processed by the log sink. If `containsPII` parameter value on the log message is true and piiSafe is false then the log message will not be processed by this log sink, if piiSafe is true then it will. If the `containsPII` parameter value is false on the log message than it is always processed by the log sink regardless of the piiSafe value.\r\n\r\nCustom log sink configuration example. In this example, the custom code executed for each log statement is to append the log into a file. Note how a Promise is returned as required:\r\n\r\n```js\r\n{\r\n\t// name of the log sink\r\n\tname: \"exampleConsoleSink\",\r\n\t// log handler function - this is where you put your custom handling code and\r\n\t// remember to always return a promise. If your operation is not asynchronous,\r\n\t// you can do this by returning Promise.resolve().\r\n\tlog: logs => {\r\n\t\tfs.appendFile(\r\n\t\t\t\"./log.txt\",\r\n\t\t\t`${JSON.stringify(logs)}`,\r\n\t\t\tconsole.log\r\n\t\t);\r\n\t\treturn Promise.resolve();\r\n\t},\r\n\t// should this log sink accept PII data, defaults to false\r\n\tpiiSafe: true,\r\n\t// log level for the sink, defaults to INFO\r\n\tlogLevel: cleLogger.logLevels.VERBOSE\r\n}\r\n```\r\n\r\nExample log sink configuration that uses a custom coded sink and both standard console and remote logging sinks:\r\n\r\n```js\r\nconst fs = require(\"fs\");\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\ncleLogger.init({\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\tname: \"fileSink\",\r\n\t\t\tlog: logs => {\r\n\t\t\t\tfs.appendFile(\"./log.txt\", `${JSON.stringify(logs)}`, console.log);\r\n\t\t\t\treturn Promise.resolve();\r\n\t\t\t},\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"remoteSink\",\r\n\t\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\r\n\t\t\tlogLevel: cleLogger.logLevels.WARN,\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"consoleSink\",\r\n\t\t\tlog: cleLogger.consoleLogger,\r\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\r\n\t\t},\r\n\t],\r\n});\r\n```\r\n\r\n### Logging statements\r\n\r\nTo create logs, you can use one of the logging function available. They all take the same arguments, described below, and log at a corresponding log level. For example, if you use the `cleLogger.verbose` logging function, and your log sink is configured with `logLevel: cleLogger.logLevels.VERBOSE`, that log sink will receive the log. If your log sink is configured with a higher log level, for example `logLevel: cleLogger.logLevels.INFO`, then it would not receive the verbose log.\r\n\r\nHere are the available logging functions:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(...);\r\ncleLogger.info(...);\r\ncleLogger.warn(...);\r\ncleLogger.error(...);\r\ncleLogger.critical(...);\r\n```\r\n\r\nThe functions are all async, return `Promise<void>`, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\r\n\r\n### Logging parameters\r\n\r\nAll logging functions take the same parameters:\r\n\r\n- `message`: the only required property, no logs will be generated without it\r\n  - type: string\r\n  - required: yes\r\n- `additionalData`: any additional log data you would like to include with the log, can be any data type\r\n  - type: any\r\n  - required: no\r\n  - default value: null\r\n- `customProperties`: a single level deep object of important properties you would like to distinguish from general log data, like various sids CLE uses it to index these properties in Application Insights. For CLE, try to use keys found in `cleLogger.customPropertyKeys`\r\n  - type: object\r\n  - required: no\r\n  - default value: {}\r\n- `componentName`: another useful way to 'tag' your logs for easier searching. Can be any string, but for CLE, try to use available options in `cleLogger.componentNames`\r\n  - type: string\r\n  - required: no\r\n  - default value: \"\"\r\n- `logCode`: log code, can be any number used to categorize logs. For defined ranges see the [log codes](#log-codes) section.\r\n  - type: number\r\n  - required: no\r\n  - default value: 0\r\n- `correlationToken`: correlation token to be associated with this log only\r\n  - type: string\r\n  - required: no\r\n  - default value: token provided in `init`, or a generated token if one wasn't provided\r\n- `containsPII`: does the log contain PII data, defaults to false. If set to true, the log will be passed in only to those log sinks that have `piiSafe` set to true. For more info see the [handling PII data](#handling-pii-data) section.\r\n  - type: boolean\r\n  - required: no\r\n  - default value: false\r\n\r\nExample verbose logging statement:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(\r\n\t\"Log message\", // message\r\n\t{ data: \"test\" }, // additionalData\r\n\t{ [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" }, // customProperties\r\n\tcleLogger.componentNames.SESSION, // componentName\r\n\t70123, // logCode\r\n\t\"my-token\", // correlationToken\r\n\ttrue, // containsPII\r\n);\r\n```\r\n\r\n### Logging constants\r\n\r\n**Logging levels**\r\n\r\nLogging level constants should be used when configuring the log level of a [log sink](#log-sinks).\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.logLevels.VERBOSE; // verbose\r\ncleLogger.logLevels.INFO; // info\r\ncleLogger.logLevels.WARN; // warn\r\ncleLogger.logLevels.ERROR; // error\r\ncleLogger.logLevels.CRITICAL; // critical\r\n```\r\n\r\n**Component names**\r\n\r\nDefines a set of commonly used components that should be used for the `componentName` parameter value of a log statement if you are logging from one of these common components in a Twilio implementation.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.componentNames.SESSION;\r\ncleLogger.componentNames.DIALER;\r\ncleLogger.componentNames.DASHBOARD;\r\ncleLogger.componentNames.AFTERCALL;\r\ncleLogger.componentNames.SUPERVISOR;\r\n```\r\n\r\n**Custom property keys**\r\n\r\nDefines a set of commonly used key that should be used when defining the custom properties object that is supplied in the `customProperties` parameter of any logging statement.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.customPropertyKeys.CHANNEL_SID;\r\ncleLogger.customPropertyKeys.TASK_SID;\r\ncleLogger.customPropertyKeys.CALL_SID;\r\ncleLogger.customPropertyKeys.WORKFLOW_SID;\r\ncleLogger.customPropertyKeys.WORKER_SID;\r\n```\r\n\r\n### Log codes\r\n\r\nIt can be helpful when investigating issues to only view logs related to specific areas of a system. Use Log Codes to give a category definition for what area of the contact center the log activity occurred within. Use numbers within the following pre-defined log code ranges or define custom log codes by using your own numbers higher than 300,000.\r\n\r\n- 100 000 - 109 999 => CALL_STATUS\r\n- 110 000 - 119 999 => IVR\r\n- 120 000 - 129 999 => ROUTING\r\n- 130 000 - 139 999 => AGENT_ACTION\r\n- 140 000 - 149 999 => TRANSFER\r\n- 150 000 - 159 999 => RM_INTEGRATION\r\n- 160 000 - 169 999 => WFM_INTEGRATION\r\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\r\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\r\n\r\n### Registering Twilio Actions\r\n\r\nIt is important to log information from the Twilio Flex UI for all implementations even when there is little customization happening. Many developers find it useful to generate log activity when some, or any, of the Twilio Actions occur within the Twilio Flex UI. Twilio Actions are events that arise from the Twilio Flex UI base implementation and indicate when certain activity is occurring - such as an agent hanging up a call. The CLE Logger library can be setup to automatically register all, or selected, Twilio Actions such that right after any code tied to the Action event has been executed a log statement is written indicating the Action occurred. This log statement will also automatically include the action event payload. To enable this logging capability, use the following approach to register the Twilio Actions. This only needs to be performed once during the initialization of the Twilio Flex UI page on the client.\r\n\r\nExample in a Twilio Flex UI plugin:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\n/**\r\n * @param flex { typeof import('@twilio/flex-ui') }\r\n * @param manager { import('@twilio/flex-ui').Manager }\r\n */\r\ninit(flex, manager) {\r\n\tcleLogger.init({\r\n\t\taccountSid: \"your-account-sid\"\r\n\t});\r\n\r\n\tcleLogger.registerTwilioActions(flex);\r\n}\r\n```\r\n\r\n**Note**: A Twilio account sid must be provided during `init` with the `accountSid` parameter, otherwise the actions won't be logged.\r\n\r\nThe logger will loop through all actions found under `flex.Actions.actions`, and attach `after` event handlers to log the action name and payload. Any private properties found in the payload (starting with '\\_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Not all Action events need to be logged and by using the `excludedFlexActionNames` property in the logger configuration the Action events not desired can be ignored. Any actions names configured in `excludedFlexActionNames` will not be included in the logs.\r\n\r\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\r\n\r\n**Twilio Actions log levels**\r\n\r\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of `INFO`.\r\n\r\n```js\r\nAcceptTask: INFO;\r\nCancelTransfer: INFO;\r\nCompleteTask: INFO;\r\nHangupCall: INFO;\r\nHideDirectory: VERBOSE;\r\nHistoryGo: VERBOSE;\r\nHistoryGoBack: VERBOSE;\r\nHistoryGoForward: VERBOSE;\r\nHistoryPush: VERBOSE;\r\nHistoryReplace: VERBOSE;\r\nHoldCall: INFO;\r\nHoldParticipant: INFO;\r\nKickParticipant: INFO;\r\nLogout: INFO;\r\nMonitorCall: INFO;\r\nNavigateToView: INFO;\r\nRejectTask: INFO;\r\nSelectTask: INFO;\r\nSelectTaskInSupervisor: INFO;\r\nSelectWorkerInSupervisor: INFO;\r\nSendMessage: INFO;\r\nSendTyping: VERBOSE;\r\nSetActivity: INFO;\r\nSetInputText: VERBOSE;\r\nShowDirectory: VERBOSE;\r\nStopMonitoringCall: INFO;\r\nToggleMute: INFO;\r\nToggleSidebar: VERBOSE;\r\nTransferTask: INFO;\r\nUnholdCall: INFO;\r\nUnholdParticipant: INFO;\r\nWrapupTask: INFO;\r\n```\r\n\r\n### Twilio Event Hooks\r\n\r\nCurrently, the Twilio console only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with `twilioEventHooks` option, and use the following functions to send event data to a custom endpoint.\r\n\r\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is `application/x-www-form-urlencoded` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// unrelated init properties omitted from this example.\r\n\t// include in the twilioEventHooks array an entry for only the event hooks\r\n\t// the executing code is going to relay. For example, if the code is only\r\n\t// handling task router web hook events then you would only need to define\r\n\t// the taskRouter relay url here.\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\r\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\r\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\r\n\t},\r\n});\r\n\r\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\r\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\r\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\r\n```\r\n\r\nExample Azure Function hook for debugger events:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\nmodule.exports = async function(context, req) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\r\n\t\t},\r\n\t});\r\n\r\n\t// log to console\r\n\tcleLogger.info(\"Received Twilio debugger event\");\r\n\r\n\t// send event data to a configured url\r\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\r\n};\r\n```\r\n\r\n**NOTE WHEN USING THIS FUNCTIONALITY IN TWILIO FUNCTIONS:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to `x-www-form-urlencoded` format.\r\n\r\nExample Twilio function task router relay using the `form-urlencoded` npm package to convert the data to `x-www-form-urlencoded` format:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\nconst formurlencoded = require(\"form-urlencoded\").default;\r\n\r\nexports.handler = async function(context, event, callback) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\r\n\t\t},\r\n\t});\r\n\r\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\r\n\r\n\tcallback();\r\n};\r\n```\r\n\r\n### Version\r\n\r\nVersion will be automatically included in all logs.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.version; // 1.0.0\r\n```\r\n\r\n### Error To Object\r\n\r\nUtility function to convert an `Error` object to a plain JS object, with all properties preserved. When serialized, `Error` objects will omit custom properties from the output which makes debugging difficult. This utility will traverse all properties in the `Error` object, and output a plain JS object with all properties including the stack trace. See an example below.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\nconst err = new Error(\"error message\");\r\nconsole.log(err); // \"Error: error message\\n    at Object.<anonymous> …\"\r\n\r\nconst errObject = cleLogger.errorToObject(err);\r\nconsole.log(errObjct); // {name: 'Error', message: 'error message', stack: 'Error: error message\\n    at Object.<anonymous> …'}\r\n```\r\n\r\nExample usage when logging errors:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ntry {\r\n\t// some code that can throw an error\r\n} catch (error) {\r\n\tcleLogger.error(\"My error message\", cleLogger.errorToObject(error));\r\n}\r\n```\r\n\r\n## Log Object\r\n\r\nThe following is the type definition of the log object received by [log sinks](#log-sinks). It consists of properties provided in log messages, as well as some that are automatically inserted like the `eventDate`. You can use this information to customize your logs inside a custom log sink (e.g. filter out unwanted logs, modify some of the properties to include more data, or any kind of conditional logic based on the value of these properties). If using the built in `remoteLogger` log processor with your custom backend, you can expect an array of these objects to be sent to your log endpoint. For more info about implementing a custom logging endpoint see the [remote logging](#remote-logging) section.\r\n\r\n```js\r\n{\r\n\t// Account sid if provided during init\r\n\taccountSid: string;\r\n\r\n\t// Any additional log data to be included with the log\r\n\tadditionalData: unknown;\r\n\r\n\t// Component name, if provided\r\n\tcomponentName: string;\r\n\r\n\t// Generated or configured token\r\n\tcorrelationToken: string;\r\n\r\n\t// Current UTC datetime in ISO 8601 format\r\n\teventDate: string;\r\n\r\n\t// Log level\r\n\tlevel: string;\r\n\r\n\t// Log code, if provided\r\n\tlogCode: number;\r\n\r\n\t// Log message\r\n\tmessage: string;\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\tproperties: object;\r\n\r\n\t// BrowserFunction if the environment is the browser\r\n\t// CloudFunction if the environment is Node.js\r\n\tsource: string;\r\n\r\n\t// Indicates that the log contains PII data\r\n\tcontainsPII: boolean;\r\n\r\n\t// Logging library version\r\n\tloggerVersion: string;\r\n}\r\n```\r\n\r\n## Handling PII Data\r\n\r\nThe logger provides support for helping to protect log messages or data that need to include PII (Personally Identifiable Information). While the logger has several mechanisms that can be used to flag logs that contain PII and block PII from going to particular log sinks, it is still ultimately the developers responsibility to ensure PII is flagged or marked appropriately for the logger to know when it exists - the logger cannot automatically detect if PII is part of a log statement or data object. The general guideline is that developers should avoid including any PII data in log information whenever possible. In the instances where PII data must be included there are several mechanisms the logger supports to help indicate PII data and protect where it is sent.\r\n\r\nA `containsPII` parameter exists on log statement methods. This flag should be set to true if there is PII data, or any potential chance of it, in the log message, additional data or custom properties data. When this flag is set to true then it will only allow log sinks that have the `piiSafe` configuration set to true to receive and process the log message. `piiSafe` indicates on a log sink whether that log sink receiver is able to internally store PII data OR has logic to strip PII data that is tagged in the message or from pre-configured additional data fields before the log is written to any storage or output device. When sending data to any `remoteLogger` endpoint the log sink can be marked `piiSafe` only as long as data is appropriately tagged in messages and/or the names of custom properties that might have PII values in additional data are pre-configured AND the endpoint implementer has verified and ensured their receiving code will remove marked PII data immediately before sending logs to any output or storage media.\r\n\r\nTo tag any part of the message parameter in a log statement as containing PII within the actual log message use the `tagPII` utility. The `tagPII` utility will indicate to a remote backend that the text between the tags should be stripped or masked before sending logs to any output or storage media.\r\n\r\nExample tagged output:\r\n\r\n```js\r\nimport { tagPII } from \"@prftesp/cle-logger\";\r\n\r\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\r\n```\r\n\r\n## Advanced Configuration\r\n\r\n### Remote logging\r\n\r\nThe library provides a built in way to transmit logs to a remote http endpoint using the `remoteLogger` logs processor. The `remoteLogger` can be directed at a known listener such as CLE or you can create your own custom log listener. In addition, the log level in effect can be controlled remotely setting the `minLogLevelOverrideUrl` to an http address that will return the current log level to use instead of any defined default logLevel on each `logSink`. The `minLogLevelOverrideUrl` can point to a known central API such as CLE or you can create your own endpoint.\r\n\r\nThe following sections outline how to create your own endpoints for either receiving log messages from the `remoteLogger` log sink or providing the current log level override to it.\r\n\r\n**Remote log endpoint**\r\n\r\nTo implement your own remote logging endpoint, all you have to do is accept `POST` requests, with a JSON payload of an array of [log objects](#log-object), and return either `{ success: true }` or `{ success: false }`. The endpoint should always return a `200 OK` status even if your server code fails so that the CLE logger does not cause failures in the business logic in which it is embedded. A good use-case would be leveraging a serverless api platform to output logs to a queue.\r\n\r\nExample of an Azure Function that can receive requests from the `remoteLogger` log sink:\r\n\r\n```js\r\nmodule.exports = async function(context, req) {\r\n\tcontext.log(req.body); // body will have the log array\r\n\r\n\tcontext.res = {\r\n\t\tstatus: 200,\r\n\t\tbody: { success: true },\r\n\t};\r\n};\r\n```\r\n\r\n**Remote log level override**\r\n\r\nThe `minLogLevelOverrideUrl` configuration property is used to provide an endpoint that accepts `GET` requests and a `accountSid` query string parameter. `accountSid` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the `minLogLevel` configuration property. If null is returned, the `minLogLevel` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a `200 OK` status code and use the `success` property to indicate failure.\r\n\r\nExample success response:\r\n\r\n```json\r\n{\r\n\t\"success\": true,\r\n\t\"minLogLevel\": \"warn\"\r\n}\r\n```\r\n\r\nExample error response:\r\n\r\n```json\r\n{\r\n\t\"success\": false,\r\n\t\"minLogLevel\": null\r\n}\r\n```\r\n\r\n## Changelog\r\n\r\n### v2.1.0\r\n\r\n**New features**\r\n\r\n- Added [Error To Object](#error-to-object) method to assist users in converting error object to plain objects.\r\n\r\n### v2.0.7\r\n\r\n**New features**\r\n\r\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the call sid during a call, and automatically include it in `customProperties` log property for all logs that happen during that call (including Flex Action logs).\r\n\r\n### v2.0.5\r\n\r\n**New features**\r\n\r\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the worker sid and automatically include it in `customProperties` log property.\r\n\r\n**Other updates**\r\n\r\n- Updated documentation based on user feedback.\r\n\r\n### v2.0.4\r\n\r\n**Bug fixes**\r\n\r\n- Fixed issue with logging to console in Twilio Functions. The `consoleLogger` will now output the log object as serialized json.\r\n\r\n### v2.0.0 [2019-12-20]\r\n\r\n**New features**\r\n\r\n- All logging output is now configured using the `logSinks` property. The library provides two commonly used loggers - `consoleLogger` and a `remoteLogger` that accepts the log url as a parameter. [Configuration](#configuration)\r\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\r\n- Log functions have a new optional `containsPII` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\r\n- A new helper function `tagPII` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\r\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\r\n\r\n**Breaking changes**\r\n\r\n- `customLogSink` configuration renamed to `logSinks`\r\n- Removed `disableRemoteLogging` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\r\n- Removed `logToConsole` configuration. This is now configured as a log sink with a `consoleLogger` helper function.\r\n- Removed `logUrl` configuration. This is now configured as a log sink with a `remoteLogger` helper function.\r\n- Removed `minLogLevel` configuration. This is now configured per log sink.\r\n\r\n**Migration guide**\r\n\r\n```js\r\nimport cleLogger from \"@prftesp/cle-logger\";\r\n\r\n// v1.0.0 and older config\r\ncleLogger.init({\r\n\tdisableRemoteLogSinks: true, // no direct replacement\r\n\tlogToConsole: true, // replaced by the \"console logger\" sink\r\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\r\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\r\n\tcustomLogSinks: [\r\n\t\t{\r\n\t\t\tname: \"custom sink\",\r\n\t\t\tlog: logs => {\r\n\t\t\t\t/* your custom logging */\r\n\t\t\t},\r\n\t\t},\r\n\t],\r\n});\r\n\r\n// v2.0.0 config\r\ncleLogger.init({\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\tname: \"custom sink\",\r\n\t\t\tlog: logs => {\r\n\t\t\t\t/* your custom logging */\r\n\t\t\t},\r\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t\t// set to true to get feature parity with older version,\r\n\t\t\t// please consider your use case before setting\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"console logger\",\r\n\t\t\tlog: consoleLogger,\r\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t\t// set to true to get feature parity with older version,\r\n\t\t\t// please consider your use case before setting\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"remote logger\",\r\n\t\t\tlog: remoteLogger(\"http://log.output/api\"),\r\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t\t// set to true to get feature parity with older version,\r\n\t\t\t// please consider your use case before setting\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t],\r\n});\r\n```\r\n\r\n### v1.0.0 [2019-09-12]\r\n\r\n- Initial release\r\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.1.1-beta.2","_nodeVersion":"10.15.2","_npmVersion":"6.11.3","dist":{"integrity":"sha512-1VZb/jtjIynMcqmLPejs85tMSY+Jf0e/PDLYmZZRsSfYWPr27lMFhDdOzbNeZTl/pjN6kEuRbiTKXBe+qvjr7w==","shasum":"296088a91a17a0e5aa18d5ec39ac639eb2c80ae5","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.1.1-beta.2.tgz","fileCount":17,"unpackedSize":82220,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe38KvCRA9TVsSAnZWagAAgiMP/07nJvMsexfEb35EcKnV\nI4nwz+9NEn4mAFEP7hbz3pxEh425AdS/I5eSu4WUlzwfmuDb9+7zItGrQnv/\n9UTYoYG0Yfwi/aobCqqAB+syqAlH/hVffqWHyeIYhk8gmv86LnU1rAKnvpFS\nqLImVdXN4XDmoK+3v71pPdCAf3DTs7Geh2t0YHmRvd1aQsPakXNTtCOEcKeU\nz2PeFuxypBvOVE90o7UrM6QRWnWOqABi4Yl2E17YHCjBV9NEy5m2WCvU5/M8\nwKcVX2xRWdKM2IPigd3EVYZIpzhwqsFGLM9qhXODtqvtRLzJ6m0cvsy9aRb7\npsmc1IkkJLXBp1ysDYunOZpOzUXzkk1EV6MDVwgTkvmWTAkB6p/H211Q+f8G\nha57iSEkI/8/Ah3lmu4fu4dErBWq7xr7AdpSWbRwdZqQmEkOk9YaXPnwGLkR\nLOPH0DJ11/i2GEDDY9jjI2zU3z3P5mweDudeKAH2meZ685s3i0m+UWo9aBUi\nfFQk2TY/gq3eFsL/48l2mkIl5oPQjnz70X7v6JxDOyj6m9wnZBzAhgBbqFXm\nd34ts7g+dQsaa8JrUngT0n1EWsdZjjZwi9IW1PmZAIpBQjqI2UiZNUx2+9Xp\nenuK/X/89eNBJ82c5N8V9dal37tiEbu3SYaF0p0YeKKWZAM+ZOv0M5oNa/YR\nbtY8\r\n=qS2n\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDnDJxxeOnngvpb004Mnd68h+j0AuqcX8t5AYPYikjdyAiEAz/HO6JrRtOoVBvFGEr1eCoi4wtjkkQHC/zta2Iscnc0="}]},"maintainers":[{"email":"c.swartzentruber@perficient.com","name":"curtis.swartzentruber"},{"email":"darko.majkic@perficient.com","name":"darko.perficient"},{"email":"espnpm@perficient.com","name":"espnpm"},{"email":"mike.frank@perficient.com","name":"mikefrank"},{"email":"shelby.hagman@perficient.com","name":"shelby-prft"}],"_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.1.1-beta.2_1591722670570_0.21150792101744664"},"_hasShrinkwrap":false},"2.1.1-beta.3":{"name":"@prftesp/cle-logger","version":"2.1.1-beta.3","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build && npm run build:es5","build:es5":"rimraf dist-es5/* && tsc --build tsconfig.es5.json","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta","local:beta":"npm run build && npm run publish-lib-beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^25.1.4","@types/node":"^12.12.31","@typescript-eslint/eslint-plugin":"^2.25.0","@typescript-eslint/parser":"^2.25.0","eslint":"^6.8.0","eslint-config-prettier":"^6.10.1","eslint-plugin-prettier":"^3.1.2","jest":"^25.2.2","prettier":"^1.19.1","rimraf":"^3.0.2","ts-jest":"^25.2.1","typescript":"^3.8.3"},"dependencies":{"axios":"^0.19.2","serializerr":"^1.0.3"},"readme":"﻿CLE Logger\r\n==\r\n\r\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\r\n\r\n## Documentation\r\n\r\n- [Target Environment](#target-environment)\r\n- [Simple Example](#simple-example)\r\n- [Installation](#installation)\r\n- [Features](#features)\r\n- [Configuration](#configuration)\r\n- [API](#api)\r\n  - [Log sinks](#log-sinks)\r\n  - [Logging statements](#logging-statements)\r\n  - [Logging parameters](#logging-parameters)\r\n  - [Logging constants](#logging-constants)\r\n  - [Log codes](#log-codes)\r\n  - [Registering Twilio Actions](#registering-twilio-actions)\r\n  - [Twilio Event Hooks](#twilio-event-hooks)\r\n  - [Version](#version)\r\n  - [Error To Object](#error-to-object)\r\n- [Log Object](#log-object)\r\n- [Handling PII Data](#handling-pii-data)\r\n- [Advanced Configuration](#advanced-configuration)\r\n  - [Remote logging](#remote-logging)\r\n- [Changelog](#changelog)\r\n\r\n## Target Environment\r\n\r\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\r\n\r\n### ES5 Compatible Version\r\n\r\nIf you need an ES5 compatible version (for example you need IE11 support), use this package: https://www.npmjs.com/package/@prftesp/cle-logger-es5. **This package is not regularly released with the main library, and should be used only when absolutely necessary**. If you need to support IE11, don't forget to include the necessary polyfills, mainly for `Promise` support. To do this, you can include this script tag on the page, before you load this library `<script src=\"https://cdn.polyfill.io/v3/polyfill.min.js?features=es6\"></script>`, or visit `https://polyfill.io` for more options.\r\n\r\n**Please note** that the ES5 version is generated, and is sharing the same code and documentation as the main library. This is why the rest of the documentation doesn't mention the ES5 version in text or examples. The only difference is how you reference it (`@prftesp/cle-logger` vs `@prftesp/cle-logger-es5`). To install it use `npm install @prftesp/cle-logger-es5`, and to import it use `import { cleLogger } from \"@prftesp/cle-logger-es5\";`.\r\n\r\n## Simple Example\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\tname: \"console sink\",\r\n\t\t\tlog: cleLogger.consoleLogger,\r\n\t\t\tpiiSafe: true,\r\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\r\n\t\t},\r\n\t],\r\n});\r\n\r\n// will log message to console\r\ncleLogger.info(\"log message\");\r\n```\r\n\r\n## Installation\r\n\r\n```bash\r\n$ npm install @prftesp/cle-logger\r\n```\r\n\r\n## Features\r\n\r\n- Runs in Node.js and the browser\r\n- Remote logging\r\n- Multiple log sinks\r\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\r\n- Remote minimum log level overrides\r\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\r\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\r\n- Support for tagging logs with custom component names or correlation tokens\r\n- Send Twilio event hook data to a custom backend\r\n\r\n## Configuration\r\n\r\nTo configure the logger call the `init` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\r\n\r\n**Available options**\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// Twilio account sid, required for use with CLE\r\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\r\n\r\n\t// Will be associated with all logs,\r\n\t// if not provided, a unique uuid will be generated\r\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\r\n\r\n\t// An array of that will receive an array of log objects,\r\n\t// and can optionally return a promise. Please see the\r\n\t// \"Log Sinks\" section for more information.\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\t// name of the log sink\r\n\t\t\tname: \"exampleConsoleSink\",\r\n\t\t\t// log handler function\r\n\t\t\tlog: cleLogger.consoleLogger,\r\n\t\t\t// This flag defines if the log message is processed by this sink when the contains\r\n\t\t\t// PII parameter of the log statement is true. piiSafe of true indicates all log\r\n\t\t\t// messages are allowed (even when contains PII is true); piiSafe of false indicates\r\n\t\t\t// only log messages with contains PII set to false will be processed by this log sink.\r\n\t\t\t// Defaults to false\r\n\t\t\tpiiSafe: true,\r\n\t\t\t// log level for the sink, defaults to INFO\r\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\r\n\t\t},\r\n\t],\r\n\r\n\t// A way to suppress error messages if set to false, defaults to true\r\n\tlogErrorsToConsole: true,\r\n\r\n\t// If logging Twilio Flex Actions, provide a list of actions\r\n\t// to be excluded from logs\r\n\texcludedFlexActionNames: [\"SendTyping\"],\r\n\r\n\t// A custom endpoint to fetch log level overrides\r\n\t// NOTE: the provided url should always end with 'accountSid=',\r\n\t// the library will append the accountSid\r\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\r\n\r\n\t// An instance of ('@twilio/flex-ui').Manager.\r\n\t// If provided, it will be used to get the worker sid and call sid (only during a call), and\r\n\t// automatically include it in 'customProperties' log property and with every log\r\n\ttwilioFlexManager: manager,\r\n\r\n\t// To send data from Twilio event hooks, to a custom backend,\r\n\t// configure one or more endpoints for each event type\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\r\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\r\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\r\n\t},\r\n});\r\n```\r\n\r\n## API\r\n\r\nThe logger can be imported as an object in CommonJs or ES6 syntax:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n// or\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n```\r\n\r\nAlternatively, all top level members of the `cleLogger` are available as named exports and can be imported separately:\r\n\r\n```js\r\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tlogSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\r\n\r\n// vs\r\n\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ninit({\r\n\tcleLogger.logSinks: [{\r\n\t\tname: \"exampleConsoleSink\",\r\n\t\tlog: cleLogger.consoleLogger,\r\n\t\tpiiSafe: true,\r\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\r\n\t}],\r\n});\r\n\r\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\r\n```\r\n\r\n### Log sinks\r\n\r\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the `logSinks` config. The logSinks configuration defines an array of objects with each object defining the log sink activity. All provided log sinks will be executed in parallel. A log sink configuration must include the following properties:\r\n\r\n- `name`: this is an identifiable name for the logger and must be unique per log sink defined\r\n- `log`: this indicates the logic to process on each batch of logs processed by the sink. There are several built in processing functions that can be used or you can insert any code inline with this definition. The built in processing functions are `consoleLogger` which will send the log statements to the attached console or `remoteLogger` which will send the log statements to the specified url provided as a definition on the parameter (i.e. `remoteLogger(\"https://mylogger.url\"`). For a custom log processing implementation, the logic must be async operations and must return a Promise object. The custom log method will receive an array of log objects for you to handle. You can find out more about the log object type in the [Log Object](#log-object) section. Here is an example of a custom log function:\r\n\r\n```js\r\nlogs => {\r\n\t// log each received log to the console separately\r\n\tlogs.forEach(log => console.log(log));\r\n\treturn Promise.resolve();\r\n};\r\n```\r\n\r\n- `logLevel`: defines the minimum default log level that will be processed by this log sink. Log levels lower than the minimum level will not be processed by this log sink. The minimum default log level can be overridden at run time by the value returned from the overall `minLogLevelOverrideUrl` that is set up in the library `init`.\r\n- `piiSafe`: indicates if log messages with a `containsPII` parameter value of true should be processed by the log sink. If `containsPII` parameter value on the log message is true and piiSafe is false then the log message will not be processed by this log sink, if piiSafe is true then it will. If the `containsPII` parameter value is false on the log message than it is always processed by the log sink regardless of the piiSafe value.\r\n\r\nCustom log sink configuration example. In this example, the custom code executed for each log statement is to append the log into a file. Note how a Promise is returned as required:\r\n\r\n```js\r\n{\r\n\t// name of the log sink\r\n\tname: \"exampleConsoleSink\",\r\n\t// log handler function - this is where you put your custom handling code and\r\n\t// remember to always return a promise. If your operation is not asynchronous,\r\n\t// you can do this by returning Promise.resolve().\r\n\tlog: logs => {\r\n\t\tfs.appendFile(\r\n\t\t\t\"./log.txt\",\r\n\t\t\t`${JSON.stringify(logs)}`,\r\n\t\t\tconsole.log\r\n\t\t);\r\n\t\treturn Promise.resolve();\r\n\t},\r\n\t// should this log sink accept PII data, defaults to false\r\n\tpiiSafe: true,\r\n\t// log level for the sink, defaults to INFO\r\n\tlogLevel: cleLogger.logLevels.VERBOSE\r\n}\r\n```\r\n\r\nExample log sink configuration that uses a custom coded sink and both standard console and remote logging sinks:\r\n\r\n```js\r\nconst fs = require(\"fs\");\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\ncleLogger.init({\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\tname: \"fileSink\",\r\n\t\t\tlog: logs => {\r\n\t\t\t\tfs.appendFile(\"./log.txt\", `${JSON.stringify(logs)}`, console.log);\r\n\t\t\t\treturn Promise.resolve();\r\n\t\t\t},\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"remoteSink\",\r\n\t\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\r\n\t\t\tlogLevel: cleLogger.logLevels.WARN,\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"consoleSink\",\r\n\t\t\tlog: cleLogger.consoleLogger,\r\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\r\n\t\t},\r\n\t],\r\n});\r\n```\r\n\r\n### Logging statements\r\n\r\nTo create logs, you can use one of the logging function available. They all take the same arguments, described below, and log at a corresponding log level. For example, if you use the `cleLogger.verbose` logging function, and your log sink is configured with `logLevel: cleLogger.logLevels.VERBOSE`, that log sink will receive the log. If your log sink is configured with a higher log level, for example `logLevel: cleLogger.logLevels.INFO`, then it would not receive the verbose log.\r\n\r\nHere are the available logging functions:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(...);\r\ncleLogger.info(...);\r\ncleLogger.warn(...);\r\ncleLogger.error(...);\r\ncleLogger.critical(...);\r\n```\r\n\r\nThe functions are all async, return `Promise<void>`, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\r\n\r\n### Logging parameters\r\n\r\nAll logging functions take the same parameters:\r\n\r\n- `message`: the only required property, no logs will be generated without it\r\n  - type: string\r\n  - required: yes\r\n- `additionalData`: any additional log data you would like to include with the log, can be any data type\r\n  - type: any\r\n  - required: no\r\n  - default value: null\r\n- `customProperties`: a single level deep object of important properties you would like to distinguish from general log data, like various sids CLE uses it to index these properties in Application Insights. For CLE, try to use keys found in `cleLogger.customPropertyKeys`\r\n  - type: object\r\n  - required: no\r\n  - default value: {}\r\n- `componentName`: another useful way to 'tag' your logs for easier searching. Can be any string, but for CLE, try to use available options in `cleLogger.componentNames`\r\n  - type: string\r\n  - required: no\r\n  - default value: \"\"\r\n- `logCode`: log code, can be any number used to categorize logs. For defined ranges see the [log codes](#log-codes) section.\r\n  - type: number\r\n  - required: no\r\n  - default value: 0\r\n- `correlationToken`: correlation token to be associated with this log only\r\n  - type: string\r\n  - required: no\r\n  - default value: token provided in `init`, or a generated token if one wasn't provided\r\n- `containsPII`: does the log contain PII data, defaults to false. If set to true, the log will be passed in only to those log sinks that have `piiSafe` set to true. For more info see the [handling PII data](#handling-pii-data) section.\r\n  - type: boolean\r\n  - required: no\r\n  - default value: false\r\n\r\nExample verbose logging statement:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.verbose(\r\n\t\"Log message\", // message\r\n\t{ data: \"test\" }, // additionalData\r\n\t{ [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" }, // customProperties\r\n\tcleLogger.componentNames.SESSION, // componentName\r\n\t70123, // logCode\r\n\t\"my-token\", // correlationToken\r\n\ttrue, // containsPII\r\n);\r\n```\r\n\r\n### Logging constants\r\n\r\n**Logging levels**\r\n\r\nLogging level constants should be used when configuring the log level of a [log sink](#log-sinks).\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.logLevels.VERBOSE; // verbose\r\ncleLogger.logLevels.INFO; // info\r\ncleLogger.logLevels.WARN; // warn\r\ncleLogger.logLevels.ERROR; // error\r\ncleLogger.logLevels.CRITICAL; // critical\r\n```\r\n\r\n**Component names**\r\n\r\nDefines a set of commonly used components that should be used for the `componentName` parameter value of a log statement if you are logging from one of these common components in a Twilio implementation.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.componentNames.SESSION;\r\ncleLogger.componentNames.DIALER;\r\ncleLogger.componentNames.DASHBOARD;\r\ncleLogger.componentNames.AFTERCALL;\r\ncleLogger.componentNames.SUPERVISOR;\r\n```\r\n\r\n**Custom property keys**\r\n\r\nDefines a set of commonly used key that should be used when defining the custom properties object that is supplied in the `customProperties` parameter of any logging statement.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.customPropertyKeys.CHANNEL_SID;\r\ncleLogger.customPropertyKeys.TASK_SID;\r\ncleLogger.customPropertyKeys.CALL_SID;\r\ncleLogger.customPropertyKeys.WORKFLOW_SID;\r\ncleLogger.customPropertyKeys.WORKER_SID;\r\n```\r\n\r\n### Log codes\r\n\r\nIt can be helpful when investigating issues to only view logs related to specific areas of a system. Use Log Codes to give a category definition for what area of the contact center the log activity occurred within. Use numbers within the following pre-defined log code ranges or define custom log codes by using your own numbers higher than 300,000.\r\n\r\n- 100 000 - 109 999 => CALL_STATUS\r\n- 110 000 - 119 999 => IVR\r\n- 120 000 - 129 999 => ROUTING\r\n- 130 000 - 139 999 => AGENT_ACTION\r\n- 140 000 - 149 999 => TRANSFER\r\n- 150 000 - 159 999 => RM_INTEGRATION\r\n- 160 000 - 169 999 => WFM_INTEGRATION\r\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\r\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\r\n\r\n### Registering Twilio Actions\r\n\r\nIt is important to log information from the Twilio Flex UI for all implementations even when there is little customization happening. Many developers find it useful to generate log activity when some, or any, of the Twilio Actions occur within the Twilio Flex UI. Twilio Actions are events that arise from the Twilio Flex UI base implementation and indicate when certain activity is occurring - such as an agent hanging up a call. The CLE Logger library can be setup to automatically register all, or selected, Twilio Actions such that right after any code tied to the Action event has been executed a log statement is written indicating the Action occurred. This log statement will also automatically include the action event payload. To enable this logging capability, use the following approach to register the Twilio Actions. This only needs to be performed once during the initialization of the Twilio Flex UI page on the client.\r\n\r\nExample in a Twilio Flex UI plugin:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\n/**\r\n * @param flex { typeof import('@twilio/flex-ui') }\r\n * @param manager { import('@twilio/flex-ui').Manager }\r\n */\r\ninit(flex, manager) {\r\n\tcleLogger.init({\r\n\t\taccountSid: \"your-account-sid\"\r\n\t});\r\n\r\n\tcleLogger.registerTwilioActions(flex);\r\n}\r\n```\r\n\r\n**Note**: A Twilio account sid must be provided during `init` with the `accountSid` parameter, otherwise the actions won't be logged.\r\n\r\nThe logger will loop through all actions found under `flex.Actions.actions`, and attach `after` event handlers to log the action name and payload. Any private properties found in the payload (starting with '\\_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Not all Action events need to be logged and by using the `excludedFlexActionNames` property in the logger configuration the Action events not desired can be ignored. Any actions names configured in `excludedFlexActionNames` will not be included in the logs.\r\n\r\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\r\n\r\n**Twilio Actions log levels**\r\n\r\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of `INFO`.\r\n\r\n```js\r\nAcceptTask: INFO;\r\nCancelTransfer: INFO;\r\nCompleteTask: INFO;\r\nHangupCall: INFO;\r\nHideDirectory: VERBOSE;\r\nHistoryGo: VERBOSE;\r\nHistoryGoBack: VERBOSE;\r\nHistoryGoForward: VERBOSE;\r\nHistoryPush: VERBOSE;\r\nHistoryReplace: VERBOSE;\r\nHoldCall: INFO;\r\nHoldParticipant: INFO;\r\nKickParticipant: INFO;\r\nLogout: INFO;\r\nMonitorCall: INFO;\r\nNavigateToView: INFO;\r\nRejectTask: INFO;\r\nSelectTask: INFO;\r\nSelectTaskInSupervisor: INFO;\r\nSelectWorkerInSupervisor: INFO;\r\nSendMessage: INFO;\r\nSendTyping: VERBOSE;\r\nSetActivity: INFO;\r\nSetInputText: VERBOSE;\r\nShowDirectory: VERBOSE;\r\nStopMonitoringCall: INFO;\r\nToggleMute: INFO;\r\nToggleSidebar: VERBOSE;\r\nTransferTask: INFO;\r\nUnholdCall: INFO;\r\nUnholdParticipant: INFO;\r\nWrapupTask: INFO;\r\n```\r\n\r\n### Twilio Event Hooks\r\n\r\nCurrently, the Twilio console only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with `twilioEventHooks` option, and use the following functions to send event data to a custom endpoint.\r\n\r\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is `application/x-www-form-urlencoded` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.init({\r\n\t// unrelated init properties omitted from this example.\r\n\t// include in the twilioEventHooks array an entry for only the event hooks\r\n\t// the executing code is going to relay. For example, if the code is only\r\n\t// handling task router web hook events then you would only need to define\r\n\t// the taskRouter relay url here.\r\n\ttwilioEventHooks: {\r\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\r\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\r\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\r\n\t},\r\n});\r\n\r\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\r\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\r\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\r\n```\r\n\r\nExample Azure Function hook for debugger events:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\n\r\nmodule.exports = async function(context, req) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\r\n\t\t},\r\n\t});\r\n\r\n\t// log to console\r\n\tcleLogger.info(\"Received Twilio debugger event\");\r\n\r\n\t// send event data to a configured url\r\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\r\n};\r\n```\r\n\r\n**NOTE WHEN USING THIS FUNCTIONALITY IN TWILIO FUNCTIONS:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to `x-www-form-urlencoded` format.\r\n\r\nExample Twilio function task router relay using the `form-urlencoded` npm package to convert the data to `x-www-form-urlencoded` format:\r\n\r\n```js\r\nconst cleLogger = require(\"@prftesp/cle-logger\");\r\nconst formurlencoded = require(\"form-urlencoded\").default;\r\n\r\nexports.handler = async function(context, event, callback) {\r\n\tcleLogger.init({\r\n\t\ttwilioEventHooks: {\r\n\t\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\r\n\t\t},\r\n\t});\r\n\r\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\r\n\r\n\tcallback();\r\n};\r\n```\r\n\r\n### Version\r\n\r\nVersion will be automatically included in all logs.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ncleLogger.version; // 1.0.0\r\n```\r\n\r\n### Error To Object\r\n\r\nUtility function to convert an `Error` object to a plain JS object, with all properties preserved. When serialized, `Error` objects will omit custom properties from the output which makes debugging difficult. This utility will traverse all properties in the `Error` object, and output a plain JS object with all properties including the stack trace. See an example below.\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\nconst err = new Error(\"error message\");\r\nconsole.log(err); // \"Error: error message\\n    at Object.<anonymous> …\"\r\n\r\nconst errObject = cleLogger.errorToObject(err);\r\nconsole.log(errObjct); // {name: 'Error', message: 'error message', stack: 'Error: error message\\n    at Object.<anonymous> …'}\r\n```\r\n\r\nExample usage when logging errors:\r\n\r\n```js\r\nimport { cleLogger } from \"@prftesp/cle-logger\";\r\n\r\ntry {\r\n\t// some code that can throw an error\r\n} catch (error) {\r\n\tcleLogger.error(\"My error message\", cleLogger.errorToObject(error));\r\n}\r\n```\r\n\r\n## Log Object\r\n\r\nThe following is the type definition of the log object received by [log sinks](#log-sinks). It consists of properties provided in log messages, as well as some that are automatically inserted like the `eventDate`. You can use this information to customize your logs inside a custom log sink (e.g. filter out unwanted logs, modify some of the properties to include more data, or any kind of conditional logic based on the value of these properties). If using the built in `remoteLogger` log processor with your custom backend, you can expect an array of these objects to be sent to your log endpoint. For more info about implementing a custom logging endpoint see the [remote logging](#remote-logging) section.\r\n\r\n```js\r\n{\r\n\t// Account sid if provided during init\r\n\taccountSid: string;\r\n\r\n\t// Any additional log data to be included with the log\r\n\tadditionalData: unknown;\r\n\r\n\t// Component name, if provided\r\n\tcomponentName: string;\r\n\r\n\t// Generated or configured token\r\n\tcorrelationToken: string;\r\n\r\n\t// Current UTC datetime in ISO 8601 format\r\n\teventDate: string;\r\n\r\n\t// Log level\r\n\tlevel: string;\r\n\r\n\t// Log code, if provided\r\n\tlogCode: number;\r\n\r\n\t// Log message\r\n\tmessage: string;\r\n\r\n\t// A single level deep object of important properties you would\r\n\t// like to distinguish from general log data, like various sids\r\n\tproperties: object;\r\n\r\n\t// BrowserFunction if the environment is the browser\r\n\t// CloudFunction if the environment is Node.js\r\n\tsource: string;\r\n\r\n\t// Indicates that the log contains PII data\r\n\tcontainsPII: boolean;\r\n\r\n\t// Logging library version\r\n\tloggerVersion: string;\r\n}\r\n```\r\n\r\n## Handling PII Data\r\n\r\nThe logger provides support for helping to protect log messages or data that need to include PII (Personally Identifiable Information). While the logger has several mechanisms that can be used to flag logs that contain PII and block PII from going to particular log sinks, it is still ultimately the developers responsibility to ensure PII is flagged or marked appropriately for the logger to know when it exists - the logger cannot automatically detect if PII is part of a log statement or data object. The general guideline is that developers should avoid including any PII data in log information whenever possible. In the instances where PII data must be included there are several mechanisms the logger supports to help indicate PII data and protect where it is sent.\r\n\r\nA `containsPII` parameter exists on log statement methods. This flag should be set to true if there is PII data, or any potential chance of it, in the log message, additional data or custom properties data. When this flag is set to true then it will only allow log sinks that have the `piiSafe` configuration set to true to receive and process the log message. `piiSafe` indicates on a log sink whether that log sink receiver is able to internally store PII data OR has logic to strip PII data that is tagged in the message or from pre-configured additional data fields before the log is written to any storage or output device. When sending data to any `remoteLogger` endpoint the log sink can be marked `piiSafe` only as long as data is appropriately tagged in messages and/or the names of custom properties that might have PII values in additional data are pre-configured AND the endpoint implementer has verified and ensured their receiving code will remove marked PII data immediately before sending logs to any output or storage media.\r\n\r\nTo tag any part of the message parameter in a log statement as containing PII within the actual log message use the `tagPII` utility. The `tagPII` utility will indicate to a remote backend that the text between the tags should be stripped or masked before sending logs to any output or storage media.\r\n\r\nExample tagged output:\r\n\r\n```js\r\nimport { tagPII } from \"@prftesp/cle-logger\";\r\n\r\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\r\n```\r\n\r\n## Advanced Configuration\r\n\r\n### Remote logging\r\n\r\nThe library provides a built in way to transmit logs to a remote http endpoint using the `remoteLogger` logs processor. The `remoteLogger` can be directed at a known listener such as CLE or you can create your own custom log listener. In addition, the log level in effect can be controlled remotely setting the `minLogLevelOverrideUrl` to an http address that will return the current log level to use instead of any defined default logLevel on each `logSink`. The `minLogLevelOverrideUrl` can point to a known central API such as CLE or you can create your own endpoint.\r\n\r\nThe following sections outline how to create your own endpoints for either receiving log messages from the `remoteLogger` log sink or providing the current log level override to it.\r\n\r\n**Remote log endpoint**\r\n\r\nTo implement your own remote logging endpoint, all you have to do is accept `POST` requests, with a JSON payload of an array of [log objects](#log-object), and return either `{ success: true }` or `{ success: false }`. The endpoint should always return a `200 OK` status even if your server code fails so that the CLE logger does not cause failures in the business logic in which it is embedded. A good use-case would be leveraging a serverless api platform to output logs to a queue.\r\n\r\nExample of an Azure Function that can receive requests from the `remoteLogger` log sink:\r\n\r\n```js\r\nmodule.exports = async function(context, req) {\r\n\tcontext.log(req.body); // body will have the log array\r\n\r\n\tcontext.res = {\r\n\t\tstatus: 200,\r\n\t\tbody: { success: true },\r\n\t};\r\n};\r\n```\r\n\r\n**Remote log level override**\r\n\r\nThe `minLogLevelOverrideUrl` configuration property is used to provide an endpoint that accepts `GET` requests and a `accountSid` query string parameter. `accountSid` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the `minLogLevel` configuration property. If null is returned, the `minLogLevel` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a `200 OK` status code and use the `success` property to indicate failure.\r\n\r\nExample success response:\r\n\r\n```json\r\n{\r\n\t\"success\": true,\r\n\t\"minLogLevel\": \"warn\"\r\n}\r\n```\r\n\r\nExample error response:\r\n\r\n```json\r\n{\r\n\t\"success\": false,\r\n\t\"minLogLevel\": null\r\n}\r\n```\r\n\r\n## Changelog\r\n\r\n### v2.1.0\r\n\r\n**New features**\r\n\r\n- Added [Error To Object](#error-to-object) method to assist users in converting error object to plain objects.\r\n\r\n### v2.0.7\r\n\r\n**New features**\r\n\r\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the call sid during a call, and automatically include it in `customProperties` log property for all logs that happen during that call (including Flex Action logs).\r\n\r\n### v2.0.5\r\n\r\n**New features**\r\n\r\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the worker sid and automatically include it in `customProperties` log property.\r\n\r\n**Other updates**\r\n\r\n- Updated documentation based on user feedback.\r\n\r\n### v2.0.4\r\n\r\n**Bug fixes**\r\n\r\n- Fixed issue with logging to console in Twilio Functions. The `consoleLogger` will now output the log object as serialized json.\r\n\r\n### v2.0.0 [2019-12-20]\r\n\r\n**New features**\r\n\r\n- All logging output is now configured using the `logSinks` property. The library provides two commonly used loggers - `consoleLogger` and a `remoteLogger` that accepts the log url as a parameter. [Configuration](#configuration)\r\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\r\n- Log functions have a new optional `containsPII` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\r\n- A new helper function `tagPII` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\r\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\r\n\r\n**Breaking changes**\r\n\r\n- `customLogSink` configuration renamed to `logSinks`\r\n- Removed `disableRemoteLogging` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\r\n- Removed `logToConsole` configuration. This is now configured as a log sink with a `consoleLogger` helper function.\r\n- Removed `logUrl` configuration. This is now configured as a log sink with a `remoteLogger` helper function.\r\n- Removed `minLogLevel` configuration. This is now configured per log sink.\r\n\r\n**Migration guide**\r\n\r\n```js\r\nimport cleLogger from \"@prftesp/cle-logger\";\r\n\r\n// v1.0.0 and older config\r\ncleLogger.init({\r\n\tdisableRemoteLogSinks: true, // no direct replacement\r\n\tlogToConsole: true, // replaced by the \"console logger\" sink\r\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\r\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\r\n\tcustomLogSinks: [\r\n\t\t{\r\n\t\t\tname: \"custom sink\",\r\n\t\t\tlog: logs => {\r\n\t\t\t\t/* your custom logging */\r\n\t\t\t},\r\n\t\t},\r\n\t],\r\n});\r\n\r\n// v2.0.0 config\r\ncleLogger.init({\r\n\tlogSinks: [\r\n\t\t{\r\n\t\t\tname: \"custom sink\",\r\n\t\t\tlog: logs => {\r\n\t\t\t\t/* your custom logging */\r\n\t\t\t},\r\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t\t// set to true to get feature parity with older version,\r\n\t\t\t// please consider your use case before setting\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"console logger\",\r\n\t\t\tlog: consoleLogger,\r\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t\t// set to true to get feature parity with older version,\r\n\t\t\t// please consider your use case before setting\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t\t{\r\n\t\t\tname: \"remote logger\",\r\n\t\t\tlog: remoteLogger(\"http://log.output/api\"),\r\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\r\n\t\t\t// set to true to get feature parity with older version,\r\n\t\t\t// please consider your use case before setting\r\n\t\t\tpiiSafe: true,\r\n\t\t},\r\n\t],\r\n});\r\n```\r\n\r\n### v1.0.0 [2019-09-12]\r\n\r\n- Initial release\r\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.1.1-beta.3","_nodeVersion":"10.15.2","_npmVersion":"6.11.3","dist":{"integrity":"sha512-l/ZP+XfooSUpuJf1eKSTtRpg9wYXX78YDGIfGdg670z6gJG/zGbYwfzue7SLYg6TlSIjd1lZ7PY0632PxIWNyQ==","shasum":"e7469eee4bcb839b560b066498f3e7654b718437","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.1.1-beta.3.tgz","fileCount":17,"unpackedSize":82464,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe38X+CRA9TVsSAnZWagAABXwQAIl9lB0fLuR30E6CpzKO\nakxPsCWfgCeC6MXMMVhJYlmBblp1nfhAbkGw/QOAoR+3AFPbEYROYiNbNlii\nGTUFqswGN51IC7ilM21JCIkc9VLaPbZkYuOk58EvsFXg/W02mj8ujE8Kd0kO\nAyrd5NpBn0h9uDx11G13W69DC5zyzXge9yjDosoNPSBkTqEf6/YbYqATh+99\nPBzwcgxuQ0qVPne1UHQ18aWc3O1pLs9TAr2HhD4hUybb2aCQp7J0YO9/J8Fr\nlfSc351KeqYohUTvNHFZPak9xDWAvTssebEk8ofIqunYajzy/C+pOOp3aP07\neGpHznKLgryE0sKVry4n2oJuMKhk9wWnMU1W2lu2a3MP8gIiEjwFkBBBDZ15\nLLzH7dUuzB9gXa1Y4xHNN76R8ZROXy4Hz4n2L6fIzQMFPDoR62FaohrrictP\nmj0/mrJKt07ZK3PHKCKYPZHMLLPB6VsVQjo5t8l/02e32IaL1ncCl750sksk\nGXQIxGmr9V6XH/CQul7RsRqfiLSjigSBUnhWioleOPPuiaPv6Q7YyMwq+5Jb\nB1YlxVbtTrbVKXNZ0txZ4GqpF6yGZcqYqSDKWXITsIUJp+YVZcmADfmiS5D6\nB1pFhIEnx3Ej0ib9EYJGc+10nO9lYVSTu36L4bUUKDtXYrJ2TiAps0MeS/6n\n3c5s\r\n=hy/S\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBDlhJlOlx/se3KzBn0MzCCFTCHnIUnAfEFx/0Dt+LlJAiBZ+QvsANK7vN7ZpN4nAg7PEemykxGaBTEwfCHbRGeZtA=="}]},"maintainers":[{"email":"c.swartzentruber@perficient.com","name":"curtis.swartzentruber"},{"email":"darko.majkic@perficient.com","name":"darko.perficient"},{"email":"espnpm@perficient.com","name":"espnpm"},{"email":"mike.frank@perficient.com","name":"mikefrank"},{"email":"shelby.hagman@perficient.com","name":"shelby-prft"}],"_npmUser":{"name":"darko.perficient","email":"darko.majkic@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.1.1-beta.3_1591723517585_0.7469757704457074"},"_hasShrinkwrap":false},"2.2.0-beta.20200618.2":{"name":"@prftesp/cle-logger","version":"2.2.0-beta.20200618.2","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build && npm run build:es5","build:es5":"rimraf dist-es5/* && tsc --build tsconfig.es5.json","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^25.1.4","@types/node":"^12.12.31","@typescript-eslint/eslint-plugin":"^2.25.0","@typescript-eslint/parser":"^2.25.0","eslint":"^6.8.0","eslint-config-prettier":"^6.10.1","eslint-plugin-prettier":"^3.1.2","jest":"^25.2.2","prettier":"^1.19.1","rimraf":"^3.0.2","ts-jest":"^25.2.1","typescript":"^3.8.3"},"dependencies":{"axios":"^0.19.2","serializerr":"^1.0.3"},"readme":"﻿CLE Logger\n==\n\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\n\n## Documentation\n\n- [Target Environment](#target-environment)\n- [Simple Example](#simple-example)\n- [Installation](#installation)\n- [Features](#features)\n- [Configuration](#configuration)\n- [API](#api)\n  - [Log sinks](#log-sinks)\n  - [Logging statements](#logging-statements)\n  - [Logging parameters](#logging-parameters)\n  - [Alternative logging functions](#alternative-logging-functions)\n  - [Logging constants](#logging-constants)\n  - [Log codes](#log-codes)\n  - [Registering Twilio Actions](#registering-twilio-actions)\n  - [Twilio Event Hooks](#twilio-event-hooks)\n  - [Version](#version)\n  - [Error To Object](#error-to-object)\n- [Log Object](#log-object)\n- [Handling PII Data](#handling-pii-data)\n- [Advanced Configuration](#advanced-configuration)\n  - [Remote logging](#remote-logging)\n- [Changelog](#changelog)\n\n## Target Environment\n\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\n\n### ES5 Compatible Version\n\nIf you need an ES5 compatible version (for example you need IE11 support), use this package: https://www.npmjs.com/package/@prftesp/cle-logger-es5. **This package is not regularly released with the main library, and should be used only when absolutely necessary**. If you need to support IE11, don't forget to include the necessary polyfills, mainly for `Promise` support. To do this, you can include this script tag on the page, before you load this library `<script src=\"https://cdn.polyfill.io/v3/polyfill.min.js?features=es6\"></script>`, or visit `https://polyfill.io` for more options.\n\n**Please note** that the ES5 version is generated, and is sharing the same code and documentation as the main library. This is why the rest of the documentation doesn't mention the ES5 version in text or examples. The only difference is how you reference it (`@prftesp/cle-logger` vs `@prftesp/cle-logger-es5`). To install it use `npm install @prftesp/cle-logger-es5`, and to import it use `import { cleLogger } from \"@prftesp/cle-logger-es5\";`.\n\n## Simple Example\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"console sink\",\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\tpiiSafe: true,\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n});\n\n// will log message to console\ncleLogger.info(\"log message\");\n```\n\n## Installation\n\n```bash\n$ npm install @prftesp/cle-logger\n```\n\n## Features\n\n- Runs in Node.js and the browser\n- Remote logging\n- Multiple log sinks\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\n- Remote minimum log level overrides\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\n- Support for tagging logs with custom component names or correlation tokens\n- Send Twilio event hook data to a custom backend\n\n## Configuration\n\nTo configure the logger call the `init` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\n\n**Available options**\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// Twilio account sid, required for use with CLE\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\n\n\t// Will be associated with all logs,\n\t// if not provided, a unique uuid will be generated\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\n\n\t// An array of that will receive an array of log objects,\n\t// and can optionally return a promise. Please see the\n\t// \"Log Sinks\" section for more information.\n\tlogSinks: [\n\t\t{\n\t\t\t// name of the log sink\n\t\t\tname: \"exampleConsoleSink\",\n\t\t\t// log handler function\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\t// This flag defines if the log message is processed by this sink when the contains\n\t\t\t// PII parameter of the log statement is true. piiSafe of true indicates all log\n\t\t\t// messages are allowed (even when contains PII is true); piiSafe of false indicates\n\t\t\t// only log messages with contains PII set to false will be processed by this log sink.\n\t\t\t// Defaults to false\n\t\t\tpiiSafe: true,\n\t\t\t// log level for the sink, defaults to INFO\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n\n\t// A way to suppress error messages if set to false, defaults to true\n\tlogErrorsToConsole: true,\n\n\t// If logging Twilio Flex Actions, provide a list of actions\n\t// to be excluded from logs\n\texcludedFlexActionNames: [\"SendTyping\"],\n\n\t// A custom endpoint to fetch log level overrides\n\t// NOTE: the provided url should always end with 'accountSid=',\n\t// the library will append the accountSid\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\n\n\t// An instance of ('@twilio/flex-ui').Manager.\n\t// If provided, it will be used to get the worker sid and call sid (only during a call), and\n\t// automatically include it in 'customProperties' log property and with every log\n\ttwilioFlexManager: manager,\n\n\t// To send data from Twilio event hooks, to a custom backend,\n\t// configure one or more endpoints for each event type\n\ttwilioEventHooks: {\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\n\t},\n});\n```\n\n## API\n\nThe logger can be imported as an object in CommonJs or ES6 syntax:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n// or\nconst cleLogger = require(\"@prftesp/cle-logger\");\n```\n\nAlternatively, all top level members of the `cleLogger` are available as named exports and can be imported separately:\n\n```js\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\n\ninit({\n\tlogSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: logLevels.VERBOSE\n\t}],\n});\n\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\n\n// vs\n\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ninit({\n\tcleLogger.logSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: cleLogger.consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}],\n});\n\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\n```\n\n### Log sinks\n\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the `logSinks` config. The logSinks configuration defines an array of objects with each object defining the log sink activity. All provided log sinks will be executed in parallel. A log sink configuration must include the following properties:\n\n- `name`: this is an identifiable name for the logger and must be unique per log sink defined\n- `log`: this indicates the logic to process on each batch of logs processed by the sink. There are several built in processing functions that can be used or you can insert any code inline with this definition. The built in processing functions are `consoleLogger` which will send the log statements to the attached console or `remoteLogger` which will send the log statements to the specified url provided as a definition on the parameter (i.e. `remoteLogger(\"https://mylogger.url\"`). For a custom log processing implementation, the logic must be async operations and must return a Promise object. The custom log method will receive an array of log objects for you to handle. You can find out more about the log object type in the [Log Object](#log-object) section. Here is an example of a custom log function:\n\n```js\nlogs => {\n\t// log each received log to the console separately\n\tlogs.forEach(log => console.log(log));\n\treturn Promise.resolve();\n};\n```\n\n- `logLevel`: defines the minimum default log level that will be processed by this log sink. Log levels lower than the minimum level will not be processed by this log sink. The minimum default log level can be overridden at run time by the value returned from the overall `minLogLevelOverrideUrl` that is set up in the library `init`.\n- `piiSafe`: indicates if log messages with a `containsPII` parameter value of true should be processed by the log sink. If `containsPII` parameter value on the log message is true and piiSafe is false then the log message will not be processed by this log sink, if piiSafe is true then it will. If the `containsPII` parameter value is false on the log message than it is always processed by the log sink regardless of the piiSafe value.\n\nCustom log sink configuration example. In this example, the custom code executed for each log statement is to append the log into a file. Note how a Promise is returned as required:\n\n```js\n{\n\t// name of the log sink\n\tname: \"exampleConsoleSink\",\n\t// log handler function - this is where you put your custom handling code and\n\t// remember to always return a promise. If your operation is not asynchronous,\n\t// you can do this by returning Promise.resolve().\n\tlog: logs => {\n\t\tfs.appendFile(\n\t\t\t\"./log.txt\",\n\t\t\t`${JSON.stringify(logs)}`,\n\t\t\tconsole.log\n\t\t);\n\t\treturn Promise.resolve();\n\t},\n\t// should this log sink accept PII data, defaults to false\n\tpiiSafe: true,\n\t// log level for the sink, defaults to INFO\n\tlogLevel: cleLogger.logLevels.VERBOSE\n}\n```\n\nExample log sink configuration that uses a custom coded sink and both standard console and remote logging sinks:\n\n```js\nconst fs = require(\"fs\");\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"fileSink\",\n\t\t\tlog: logs => {\n\t\t\t\tfs.appendFile(\"./log.txt\", `${JSON.stringify(logs)}`, console.log);\n\t\t\t\treturn Promise.resolve();\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"remoteSink\",\n\t\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\n\t\t\tlogLevel: cleLogger.logLevels.WARN,\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"consoleSink\",\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n});\n```\n\n### Logging statements\n\nTo create logs, you can use one of the logging function available. They all take the same arguments, described below, and log at a corresponding log level. For example, if you use the `cleLogger.verbose` logging function, and your log sink is configured with `logLevel: cleLogger.logLevels.VERBOSE`, that log sink will receive the log. If your log sink is configured with a higher log level, for example `logLevel: cleLogger.logLevels.INFO`, then it would not receive the verbose log.\n\nHere are the available logging functions:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(...);\ncleLogger.info(...);\ncleLogger.warn(...);\ncleLogger.error(...);\ncleLogger.critical(...);\n```\n\nThe functions are all async, return `Promise<void>`, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\n\n### Logging parameters\n\nAll logging functions take the same parameters:\n\n- `message`: the only required property, no logs will be generated without it\n  - type: string\n  - required: yes\n- `additionalData`: any additional log data you would like to include with the log, can be any data type\n  - type: any\n  - required: no\n  - default value: null\n- `customProperties`: a single level deep object of important properties you would like to distinguish from general log data, like various sids CLE uses it to index these properties in Application Insights. For CLE, try to use keys found in `cleLogger.customPropertyKeys`\n  - type: object\n  - required: no\n  - default value: {}\n- `componentName`: another useful way to 'tag' your logs for easier searching. Can be any string, but for CLE, try to use available options in `cleLogger.componentNames`\n  - type: string\n  - required: no\n  - default value: \"\"\n- `logCode`: log code, can be any number used to categorize logs. For defined ranges see the [log codes](#log-codes) section.\n  - type: number\n  - required: no\n  - default value: 0\n- `correlationToken`: correlation token to be associated with this log only\n  - type: string\n  - required: no\n  - default value: token provided in `init`, or a generated token if one wasn't provided\n- `containsPII`: does the log contain PII data, defaults to false. If set to true, the log will be passed in only to those log sinks that have `piiSafe` set to true. For more info see the [handling PII data](#handling-pii-data) section.\n  - type: boolean\n  - required: no\n  - default value: false\n\n### Alternative logging functions\n\nIf you would prefer to pass in an object to logging functions, instead of using a long list of parameters, you can use the new logging functions available under the `cleLogger.v2` namespace. These functions are namespaced to keep backwards compatibility, but you should feel free to use them going forward. The behavior of these functions is exactly the same as with those listed in [logging statements](#logging-statements) section.\n\nExample verbose logging statement with both versions of the logging function:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(\n\t\"Log message\", // message\n\t{ data: \"test\" }, // additionalData\n\t{ [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" }, // customProperties\n\tcleLogger.componentNames.SESSION, // componentName\n\t70123, // logCode\n\t\"my-token\", // correlationToken\n\ttrue, // containsPII\n);\n\ncleLogger.v2.verbose(\n\t\"Log message\",\n\t{\n\t\tadditionalData: { data: \"test\" },\n\t\tcustomProperties: { [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" },\n\t\tcomponentName: cleLogger.componentNames.SESSION,\n\t\tlogCode: 70123,\n\t\tcorrelationToken: \"my-token\",\n\t\tcontainsPII: true,\n\t}\n);\n```\n\nAs you can see in the examples above, both functions take in the same values, but the `v2` version makes it a bit easier to see which value corresponds to each key. It comes down to personal preference, use whichever you prefer.\n\n### Logging constants\n\n**Logging levels**\n\nLogging level constants should be used when configuring the log level of a [log sink](#log-sinks).\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.logLevels.VERBOSE; // verbose\ncleLogger.logLevels.INFO; // info\ncleLogger.logLevels.WARN; // warn\ncleLogger.logLevels.ERROR; // error\ncleLogger.logLevels.CRITICAL; // critical\n```\n\n**Component names**\n\nDefines a set of commonly used components that should be used for the `componentName` parameter value of a log statement if you are logging from one of these common components in a Twilio implementation.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.componentNames.SESSION;\ncleLogger.componentNames.DIALER;\ncleLogger.componentNames.DASHBOARD;\ncleLogger.componentNames.AFTERCALL;\ncleLogger.componentNames.SUPERVISOR;\n```\n\n**Custom property keys**\n\nDefines a set of commonly used key that should be used when defining the custom properties object that is supplied in the `customProperties` parameter of any logging statement.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.customPropertyKeys.CHANNEL_SID;\ncleLogger.customPropertyKeys.TASK_SID;\ncleLogger.customPropertyKeys.CALL_SID;\ncleLogger.customPropertyKeys.WORKFLOW_SID;\ncleLogger.customPropertyKeys.WORKER_SID;\n```\n\n### Log codes\n\nIt can be helpful when investigating issues to only view logs related to specific areas of a system. Use Log Codes to give a category definition for what area of the contact center the log activity occurred within. Use numbers within the following pre-defined log code ranges or define custom log codes by using your own numbers higher than 300,000.\n\n- 100 000 - 109 999 => CALL_STATUS\n- 110 000 - 119 999 => IVR\n- 120 000 - 129 999 => ROUTING\n- 130 000 - 139 999 => AGENT_ACTION\n- 140 000 - 149 999 => TRANSFER\n- 150 000 - 159 999 => RM_INTEGRATION\n- 160 000 - 169 999 => WFM_INTEGRATION\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\n\n### Registering Twilio Actions\n\nIt is important to log information from the Twilio Flex UI for all implementations even when there is little customization happening. Many developers find it useful to generate log activity when some, or any, of the Twilio Actions occur within the Twilio Flex UI. Twilio Actions are events that arise from the Twilio Flex UI base implementation and indicate when certain activity is occurring - such as an agent hanging up a call. The CLE Logger library can be setup to automatically register all, or selected, Twilio Actions such that right after any code tied to the Action event has been executed a log statement is written indicating the Action occurred. This log statement will also automatically include the action event payload. To enable this logging capability, use the following approach to register the Twilio Actions. This only needs to be performed once during the initialization of the Twilio Flex UI page on the client.\n\nExample in a Twilio Flex UI plugin:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n/**\n * @param flex { typeof import('@twilio/flex-ui') }\n * @param manager { import('@twilio/flex-ui').Manager }\n */\ninit(flex, manager) {\n\tcleLogger.init({\n\t\taccountSid: \"your-account-sid\"\n\t});\n\n\tcleLogger.registerTwilioActions(flex);\n}\n```\n\n**Note**: A Twilio account sid must be provided during `init` with the `accountSid` parameter, otherwise the actions won't be logged.\n\nThe logger will loop through all actions found under `flex.Actions.actions`, and attach `after` event handlers to log the action name and payload. Any private properties found in the payload (starting with '\\_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Not all Action events need to be logged and by using the `excludedFlexActionNames` property in the logger configuration the Action events not desired can be ignored. Any actions names configured in `excludedFlexActionNames` will not be included in the logs.\n\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\n\n**Twilio Actions log levels**\n\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of `INFO`.\n\n```js\nAcceptTask: INFO;\nCancelTransfer: INFO;\nCompleteTask: INFO;\nHangupCall: INFO;\nHideDirectory: VERBOSE;\nHistoryGo: VERBOSE;\nHistoryGoBack: VERBOSE;\nHistoryGoForward: VERBOSE;\nHistoryPush: VERBOSE;\nHistoryReplace: VERBOSE;\nHoldCall: INFO;\nHoldParticipant: INFO;\nKickParticipant: INFO;\nLogout: INFO;\nMonitorCall: INFO;\nNavigateToView: INFO;\nRejectTask: INFO;\nSelectTask: INFO;\nSelectTaskInSupervisor: INFO;\nSelectWorkerInSupervisor: INFO;\nSendMessage: INFO;\nSendTyping: VERBOSE;\nSetActivity: INFO;\nSetInputText: VERBOSE;\nShowDirectory: VERBOSE;\nStopMonitoringCall: INFO;\nToggleMute: INFO;\nToggleSidebar: VERBOSE;\nTransferTask: INFO;\nUnholdCall: INFO;\nUnholdParticipant: INFO;\nWrapupTask: INFO;\n```\n\n### Twilio Event Hooks\n\nCurrently, the Twilio console only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with `twilioEventHooks` option, and use the following functions to send event data to a custom endpoint.\n\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is `application/x-www-form-urlencoded` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// unrelated init properties omitted from this example.\n\t// include in the twilioEventHooks array an entry for only the event hooks\n\t// the executing code is going to relay. For example, if the code is only\n\t// handling task router web hook events then you would only need to define\n\t// the taskRouter relay url here.\n\ttwilioEventHooks: {\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\n\t},\n});\n\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\n```\n\nExample Azure Function hook for debugger events:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\nmodule.exports = async function(context, req) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\t},\n\t});\n\n\t// log to console\n\tcleLogger.info(\"Received Twilio debugger event\");\n\n\t// send event data to a configured url\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\n};\n```\n\n**NOTE WHEN USING THIS FUNCTIONALITY IN TWILIO FUNCTIONS:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to `x-www-form-urlencoded` format.\n\nExample Twilio function task router relay using the `form-urlencoded` npm package to convert the data to `x-www-form-urlencoded` format:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\nconst formurlencoded = require(\"form-urlencoded\").default;\n\nexports.handler = async function(context, event, callback) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\t},\n\t});\n\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\n\n\tcallback();\n};\n```\n\n### Version\n\nVersion will be automatically included in all logs.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.version; // 1.0.0\n```\n\n### Error To Object\n\nUtility function to convert an `Error` object to a plain JS object, with all properties preserved. When serialized, `Error` objects will omit custom properties from the output which makes debugging difficult. This utility will traverse all properties in the `Error` object, and output a plain JS object with all properties including the stack trace. See an example below.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\nconst err = new Error(\"error message\");\nconsole.log(err); // \"Error: error message\\n    at Object.<anonymous> …\"\n\nconst errObject = cleLogger.errorToObject(err);\nconsole.log(errObjct); // {name: 'Error', message: 'error message', stack: 'Error: error message\\n    at Object.<anonymous> …'}\n```\n\nExample usage when logging errors:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ntry {\n\t// some code that can throw an error\n} catch (error) {\n\tcleLogger.error(\"My error message\", cleLogger.errorToObject(error));\n}\n```\n\n## Log Object\n\nThe following is the type definition of the log object received by [log sinks](#log-sinks). It consists of properties provided in log messages, as well as some that are automatically inserted like the `eventDate`. You can use this information to customize your logs inside a custom log sink (e.g. filter out unwanted logs, modify some of the properties to include more data, or any kind of conditional logic based on the value of these properties). If using the built in `remoteLogger` log processor with your custom backend, you can expect an array of these objects to be sent to your log endpoint. For more info about implementing a custom logging endpoint see the [remote logging](#remote-logging) section.\n\n```js\n{\n\t// Account sid if provided during init\n\taccountSid: string;\n\n\t// Any additional log data to be included with the log\n\tadditionalData: unknown;\n\n\t// Component name, if provided\n\tcomponentName: string;\n\n\t// Generated or configured token\n\tcorrelationToken: string;\n\n\t// Current UTC datetime in ISO 8601 format\n\teventDate: string;\n\n\t// Log level\n\tlevel: string;\n\n\t// Log code, if provided\n\tlogCode: number;\n\n\t// Log message\n\tmessage: string;\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\tproperties: object;\n\n\t// BrowserFunction if the environment is the browser\n\t// CloudFunction if the environment is Node.js\n\tsource: string;\n\n\t// Indicates that the log contains PII data\n\tcontainsPII: boolean;\n\n\t// Logging library version\n\tloggerVersion: string;\n}\n```\n\n## Handling PII Data\n\nThe logger provides support for helping to protect log messages or data that need to include PII (Personally Identifiable Information). While the logger has several mechanisms that can be used to flag logs that contain PII and block PII from going to particular log sinks, it is still ultimately the developers responsibility to ensure PII is flagged or marked appropriately for the logger to know when it exists - the logger cannot automatically detect if PII is part of a log statement or data object. The general guideline is that developers should avoid including any PII data in log information whenever possible. In the instances where PII data must be included there are several mechanisms the logger supports to help indicate PII data and protect where it is sent.\n\nA `containsPII` parameter exists on log statement methods. This flag should be set to true if there is PII data, or any potential chance of it, in the log message, additional data or custom properties data. When this flag is set to true then it will only allow log sinks that have the `piiSafe` configuration set to true to receive and process the log message. `piiSafe` indicates on a log sink whether that log sink receiver is able to internally store PII data OR has logic to strip PII data that is tagged in the message or from pre-configured additional data fields before the log is written to any storage or output device. When sending data to any `remoteLogger` endpoint the log sink can be marked `piiSafe` only as long as data is appropriately tagged in messages and/or the names of custom properties that might have PII values in additional data are pre-configured AND the endpoint implementer has verified and ensured their receiving code will remove marked PII data immediately before sending logs to any output or storage media.\n\nTo tag any part of the message parameter in a log statement as containing PII within the actual log message use the `tagPII` utility. The `tagPII` utility will indicate to a remote backend that the text between the tags should be stripped or masked before sending logs to any output or storage media.\n\nExample tagged output:\n\n```js\nimport { tagPII } from \"@prftesp/cle-logger\";\n\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\n```\n\n## Advanced Configuration\n\n### Remote logging\n\nThe library provides a built in way to transmit logs to a remote http endpoint using the `remoteLogger` logs processor. The `remoteLogger` can be directed at a known listener such as CLE or you can create your own custom log listener. In addition, the log level in effect can be controlled remotely setting the `minLogLevelOverrideUrl` to an http address that will return the current log level to use instead of any defined default logLevel on each `logSink`. The `minLogLevelOverrideUrl` can point to a known central API such as CLE or you can create your own endpoint.\n\nThe following sections outline how to create your own endpoints for either receiving log messages from the `remoteLogger` log sink or providing the current log level override to it.\n\n**Remote log endpoint**\n\nTo implement your own remote logging endpoint, all you have to do is accept `POST` requests, with a JSON payload of an array of [log objects](#log-object), and return either `{ success: true }` or `{ success: false }`. The endpoint should always return a `200 OK` status even if your server code fails so that the CLE logger does not cause failures in the business logic in which it is embedded. A good use-case would be leveraging a serverless api platform to output logs to a queue.\n\nExample of an Azure Function that can receive requests from the `remoteLogger` log sink:\n\n```js\nmodule.exports = async function(context, req) {\n\tcontext.log(req.body); // body will have the log array\n\n\tcontext.res = {\n\t\tstatus: 200,\n\t\tbody: { success: true },\n\t};\n};\n```\n\n**Remote log level override**\n\nThe `minLogLevelOverrideUrl` configuration property is used to provide an endpoint that accepts `GET` requests and a `accountSid` query string parameter. `accountSid` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the `minLogLevel` configuration property. If null is returned, the `minLogLevel` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a `200 OK` status code and use the `success` property to indicate failure.\n\nExample success response:\n\n```json\n{\n\t\"success\": true,\n\t\"minLogLevel\": \"warn\"\n}\n```\n\nExample error response:\n\n```json\n{\n\t\"success\": false,\n\t\"minLogLevel\": null\n}\n```\n\n## Changelog\n\n### v2.2.0\n\n**New features**\n\n- Added [alternative logging functions](#alternative-logging-functions) that have a much shorter parameter list, and allow you to specify only those logging properties that you want, without the need to pass in `null` for parameters you are not interested in. New functions are available under `cleLogger.v2` for backward compatibility reasons.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n// if you want to log just the message and the token, \n// you need to default parameters to null until you get to token\ncleLogger.verbose(\"Log message\", null, null, null, null, \"my-token\");\n\n// with the new functions, you just provide an object with the properties you want\ncleLogger.v2.verbose(\"Log message\", { correlationToken: \"my-token\" });\n```\n\n### v2.1.0\n\n**New features**\n\n- Added [Error To Object](#error-to-object) method to assist users in converting error object to plain objects.\n\n### v2.0.7\n\n**New features**\n\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the call sid during a call, and automatically include it in `customProperties` log property for all logs that happen during that call (including Flex Action logs).\n\n### v2.0.5\n\n**New features**\n\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the worker sid and automatically include it in `customProperties` log property.\n\n**Other updates**\n\n- Updated documentation based on user feedback.\n\n### v2.0.4\n\n**Bug fixes**\n\n- Fixed issue with logging to console in Twilio Functions. The `consoleLogger` will now output the log object as serialized json.\n\n### v2.0.0 [2019-12-20]\n\n**New features**\n\n- All logging output is now configured using the `logSinks` property. The library provides two commonly used loggers - `consoleLogger` and a `remoteLogger` that accepts the log url as a parameter. [Configuration](#configuration)\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\n- Log functions have a new optional `containsPII` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\n- A new helper function `tagPII` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\n\n**Breaking changes**\n\n- `customLogSink` configuration renamed to `logSinks`\n- Removed `disableRemoteLogging` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\n- Removed `logToConsole` configuration. This is now configured as a log sink with a `consoleLogger` helper function.\n- Removed `logUrl` configuration. This is now configured as a log sink with a `remoteLogger` helper function.\n- Removed `minLogLevel` configuration. This is now configured per log sink.\n\n**Migration guide**\n\n```js\nimport cleLogger from \"@prftesp/cle-logger\";\n\n// v1.0.0 and older config\ncleLogger.init({\n\tdisableRemoteLogSinks: true, // no direct replacement\n\tlogToConsole: true, // replaced by the \"console logger\" sink\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\n\tcustomLogSinks: [\n\t\t{\n\t\t\tname: \"custom sink\",\n\t\t\tlog: logs => {\n\t\t\t\t/* your custom logging */\n\t\t\t},\n\t\t},\n\t],\n});\n\n// v2.0.0 config\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"custom sink\",\n\t\t\tlog: logs => {\n\t\t\t\t/* your custom logging */\n\t\t\t},\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"console logger\",\n\t\t\tlog: consoleLogger,\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"remote logger\",\n\t\t\tlog: remoteLogger(\"http://log.output/api\"),\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t],\n});\n```\n\n### v1.0.0 [2019-09-12]\n\n- Initial release\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.2.0-beta.20200618.2","_nodeVersion":"12.18.0","_npmVersion":"6.14.5","dist":{"integrity":"sha512-TuiDb7B92fgc7s60sixnedrbYpWxhWfz6u2v+EX6YmATZAk4w3oYqTP5DE1/BK1HMW2TbCFtrgBNI7kAAm9aKw==","shasum":"3941e8c82bea0e2be3ce1a331184afc310a46582","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.2.0-beta.20200618.2.tgz","fileCount":17,"unpackedSize":87023,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe658SCRA9TVsSAnZWagAAHvYP/Al1yJ00FGKl2hlSh9uS\naJcN/z1j/D4shy7QUaEoBthIwJ/tdlK0DdFquib4DipvgiFYuVsOemL6b5BA\nbgAi/nDlIVK5UcARDjwDdT6XVWiXrbvlbbzAC6NaT3KZk3ELMQHHVkmSthoz\njOJEZoJHoQt8dixDVo1SScoTXGMc/y75l4LydrCUdrVf6F/tRUMvuvuU7nc2\n/f/FQREuMEzWVojBQgir9RDq+DogweAQiK8yHI5wfZy/5KSd5Ig2wucAEO4U\nd9aM1YT80UbewkVdaE+nGv2XVEZtLfrvs36h/GvJS36zsFVr9FYOrHMuslJE\nfqO3EjET4ZB1tOTZzOc4NmoVoNPOnKkW7EDOPF7SqAL31HCFUMFAzzu9xp4G\n9O0LNJD75SrK+fPQjLInjHJKVU7YjWnSPbC7tmBbWXz8KP8gs6oaZU2NMGTh\npvaSxFrXGoepT1eI0vLHcK+AGd4pKVHHQVx6EBMwjhCzjroeu19kYOVonWJd\n67xFC1J9P36GfrAxx7K1LrKFyacvPs/dJa3AAVk7v2RWOBnKc51uEWOSxY8H\nK7tco7DqhoMKvwUlHwdTm4RwXn7/63MAk0ryL67VQob29jTqs+lGK3OFdqUm\nSzNQOeJI2gXVwm/UMVD75Ksb7j6PifN+JVQWHcSpHsWWLGa2i2i0+SMEDbKc\n/Pog\r\n=HMKA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGgMyZ1/OcRn68I31qKNTHGvXNfNf9FojoO55x8BxnYcAiEAmDgNSNE8XjG1f0U3r1ei3nWfuIQaDyeogQ5w0nmdq9c="}]},"maintainers":[{"email":"c.swartzentruber@perficient.com","name":"curtis.swartzentruber"},{"email":"darko.majkic@perficient.com","name":"darko.perficient"},{"email":"espnpm@perficient.com","name":"espnpm"},{"email":"mike.frank@perficient.com","name":"mikefrank"},{"email":"shelby.hagman@perficient.com","name":"shelby-prft"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.2.0-beta.20200618.2_1592499986234_0.048157807325354796"},"_hasShrinkwrap":false},"2.2.0-beta.20200618.4":{"name":"@prftesp/cle-logger","version":"2.2.0-beta.20200618.4","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build && npm run build:es5","build:es5":"rimraf dist-es5/* && tsc --build tsconfig.es5.json","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^25.1.4","@types/node":"^12.12.31","@typescript-eslint/eslint-plugin":"^2.25.0","@typescript-eslint/parser":"^2.25.0","eslint":"^6.8.0","eslint-config-prettier":"^6.10.1","eslint-plugin-prettier":"^3.1.2","jest":"^25.2.2","prettier":"^1.19.1","rimraf":"^3.0.2","ts-jest":"^25.2.1","typescript":"^3.8.3"},"dependencies":{"axios":"^0.19.2","serializerr":"^1.0.3"},"readme":"﻿CLE Logger\n==\n\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\n\n## Documentation\n\n- [Target Environment](#target-environment)\n- [Simple Example](#simple-example)\n- [Installation](#installation)\n- [Features](#features)\n- [Configuration](#configuration)\n- [API](#api)\n  - [Log sinks](#log-sinks)\n  - [Logging statements](#logging-statements)\n  - [Logging parameters](#logging-parameters)\n  - [Alternative logging functions](#alternative-logging-functions)\n  - [Logging constants](#logging-constants)\n  - [Log codes](#log-codes)\n  - [Registering Twilio Actions](#registering-twilio-actions)\n  - [Twilio Event Hooks](#twilio-event-hooks)\n  - [Version](#version)\n  - [Error To Object](#error-to-object)\n- [Log Object](#log-object)\n- [Handling PII Data](#handling-pii-data)\n- [Advanced Configuration](#advanced-configuration)\n  - [Remote logging](#remote-logging)\n- [Changelog](#changelog)\n\n## Target Environment\n\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\n\n### ES5 Compatible Version\n\nIf you need an ES5 compatible version (for example you need IE11 support), use this package: https://www.npmjs.com/package/@prftesp/cle-logger-es5. **This package is not regularly released with the main library, and should be used only when absolutely necessary**. If you need to support IE11, don't forget to include the necessary polyfills, mainly for `Promise` support. To do this, you can include this script tag on the page, before you load this library `<script src=\"https://cdn.polyfill.io/v3/polyfill.min.js?features=es6\"></script>`, or visit `https://polyfill.io` for more options.\n\n**Please note** that the ES5 version is generated, and is sharing the same code and documentation as the main library. This is why the rest of the documentation doesn't mention the ES5 version in text or examples. The only difference is how you reference it (`@prftesp/cle-logger` vs `@prftesp/cle-logger-es5`). To install it use `npm install @prftesp/cle-logger-es5`, and to import it use `import { cleLogger } from \"@prftesp/cle-logger-es5\";`.\n\n## Simple Example\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"console sink\",\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\tpiiSafe: true,\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n});\n\n// will log message to console\ncleLogger.info(\"log message\");\n```\n\n## Installation\n\n```bash\n$ npm install @prftesp/cle-logger\n```\n\n## Features\n\n- Runs in Node.js and the browser\n- Remote logging\n- Multiple log sinks\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\n- Remote minimum log level overrides\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\n- Support for tagging logs with custom component names or correlation tokens\n- Send Twilio event hook data to a custom backend\n\n## Configuration\n\nTo configure the logger call the `init` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\n\n**Available options**\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// Twilio account sid, required for use with CLE\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\n\n\t// Will be associated with all logs,\n\t// if not provided, a unique uuid will be generated\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\n\n\t// An array of that will receive an array of log objects,\n\t// and can optionally return a promise. Please see the\n\t// \"Log Sinks\" section for more information.\n\tlogSinks: [\n\t\t{\n\t\t\t// name of the log sink\n\t\t\tname: \"exampleConsoleSink\",\n\t\t\t// log handler function\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\t// This flag defines if the log message is processed by this sink when the contains\n\t\t\t// PII parameter of the log statement is true. piiSafe of true indicates all log\n\t\t\t// messages are allowed (even when contains PII is true); piiSafe of false indicates\n\t\t\t// only log messages with contains PII set to false will be processed by this log sink.\n\t\t\t// Defaults to false\n\t\t\tpiiSafe: true,\n\t\t\t// log level for the sink, defaults to INFO\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n\n\t// A way to suppress error messages if set to false, defaults to true\n\tlogErrorsToConsole: true,\n\n\t// If logging Twilio Flex Actions, provide a list of actions\n\t// to be excluded from logs\n\texcludedFlexActionNames: [\"SendTyping\"],\n\n\t// A custom endpoint to fetch log level overrides\n\t// NOTE: the provided url should always end with 'accountSid=',\n\t// the library will append the accountSid\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\n\n\t// An instance of ('@twilio/flex-ui').Manager.\n\t// If provided, it will be used to get the worker sid and call sid (only during a call), and\n\t// automatically include it in 'customProperties' log property and with every log\n\ttwilioFlexManager: manager,\n\n\t// To send data from Twilio event hooks, to a custom backend,\n\t// configure one or more endpoints for each event type\n\ttwilioEventHooks: {\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\n\t},\n});\n```\n\n## API\n\nThe logger can be imported as an object in CommonJs or ES6 syntax:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n// or\nconst cleLogger = require(\"@prftesp/cle-logger\");\n```\n\nAlternatively, all top level members of the `cleLogger` are available as named exports and can be imported separately:\n\n```js\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\n\ninit({\n\tlogSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: logLevels.VERBOSE\n\t}],\n});\n\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\n\n// vs\n\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ninit({\n\tcleLogger.logSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: cleLogger.consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}],\n});\n\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\n```\n\n### Log sinks\n\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the `logSinks` config. The logSinks configuration defines an array of objects with each object defining the log sink activity. All provided log sinks will be executed in parallel. A log sink configuration must include the following properties:\n\n- `name`: this is an identifiable name for the logger and must be unique per log sink defined\n- `log`: this indicates the logic to process on each batch of logs processed by the sink. There are several built in processing functions that can be used or you can insert any code inline with this definition. The built in processing functions are `consoleLogger` which will send the log statements to the attached console or `remoteLogger` which will send the log statements to the specified url provided as a definition on the parameter (i.e. `remoteLogger(\"https://mylogger.url\"`). For a custom log processing implementation, the logic must be async operations and must return a Promise object. The custom log method will receive an array of log objects for you to handle. You can find out more about the log object type in the [Log Object](#log-object) section. Here is an example of a custom log function:\n\n```js\nlogs => {\n\t// log each received log to the console separately\n\tlogs.forEach(log => console.log(log));\n\treturn Promise.resolve();\n};\n```\n\n- `logLevel`: defines the minimum default log level that will be processed by this log sink. Log levels lower than the minimum level will not be processed by this log sink. The minimum default log level can be overridden at run time by the value returned from the overall `minLogLevelOverrideUrl` that is set up in the library `init`.\n- `piiSafe`: indicates if log messages with a `containsPII` parameter value of true should be processed by the log sink. If `containsPII` parameter value on the log message is true and piiSafe is false then the log message will not be processed by this log sink, if piiSafe is true then it will. If the `containsPII` parameter value is false on the log message than it is always processed by the log sink regardless of the piiSafe value.\n\nCustom log sink configuration example. In this example, the custom code executed for each log statement is to append the log into a file. Note how a Promise is returned as required:\n\n```js\n{\n\t// name of the log sink\n\tname: \"exampleConsoleSink\",\n\t// log handler function - this is where you put your custom handling code and\n\t// remember to always return a promise. If your operation is not asynchronous,\n\t// you can do this by returning Promise.resolve().\n\tlog: logs => {\n\t\tfs.appendFile(\n\t\t\t\"./log.txt\",\n\t\t\t`${JSON.stringify(logs)}`,\n\t\t\tconsole.log\n\t\t);\n\t\treturn Promise.resolve();\n\t},\n\t// should this log sink accept PII data, defaults to false\n\tpiiSafe: true,\n\t// log level for the sink, defaults to INFO\n\tlogLevel: cleLogger.logLevels.VERBOSE\n}\n```\n\nExample log sink configuration that uses a custom coded sink and both standard console and remote logging sinks:\n\n```js\nconst fs = require(\"fs\");\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"fileSink\",\n\t\t\tlog: logs => {\n\t\t\t\tfs.appendFile(\"./log.txt\", `${JSON.stringify(logs)}`, console.log);\n\t\t\t\treturn Promise.resolve();\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"remoteSink\",\n\t\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\n\t\t\tlogLevel: cleLogger.logLevels.WARN,\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"consoleSink\",\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n});\n```\n\n### Logging statements\n\nTo create logs, you can use one of the logging function available. They all take the same arguments, described below, and log at a corresponding log level. For example, if you use the `cleLogger.verbose` logging function, and your log sink is configured with `logLevel: cleLogger.logLevels.VERBOSE`, that log sink will receive the log. If your log sink is configured with a higher log level, for example `logLevel: cleLogger.logLevels.INFO`, then it would not receive the verbose log.\n\nHere are the available logging functions:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(...);\ncleLogger.info(...);\ncleLogger.warn(...);\ncleLogger.error(...);\ncleLogger.critical(...);\n```\n\nThe functions are all async, return `Promise<void>`, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\n\n### Logging parameters\n\nAll logging functions take the same parameters:\n\n- `message`: the only required property, no logs will be generated without it\n  - type: string\n  - required: yes\n- `additionalData`: any additional log data you would like to include with the log, can be any data type\n  - type: any\n  - required: no\n  - default value: null\n- `customProperties`: a single level deep object of important properties you would like to distinguish from general log data, like various sids CLE uses it to index these properties in Application Insights. For CLE, try to use keys found in `cleLogger.customPropertyKeys`\n  - type: object\n  - required: no\n  - default value: {}\n- `componentName`: another useful way to 'tag' your logs for easier searching. Can be any string, but for CLE, try to use available options in `cleLogger.componentNames`\n  - type: string\n  - required: no\n  - default value: \"\"\n- `logCode`: log code, can be any number used to categorize logs. For defined ranges see the [log codes](#log-codes) section.\n  - type: number\n  - required: no\n  - default value: 0\n- `correlationToken`: correlation token to be associated with this log only\n  - type: string\n  - required: no\n  - default value: token provided in `init`, or a generated token if one wasn't provided\n- `containsPII`: does the log contain PII data, defaults to false. If set to true, the log will be passed in only to those log sinks that have `piiSafe` set to true. For more info see the [handling PII data](#handling-pii-data) section.\n  - type: boolean\n  - required: no\n  - default value: false\n\n### Alternative logging functions\n\nIf you would prefer to pass in an object to logging functions, instead of using a long list of parameters, you can use the new logging functions available under the `cleLogger.v2` namespace. These functions are namespaced to keep backwards compatibility, but you should feel free to use them going forward. The behavior of these functions is exactly the same as with those listed in [logging statements](#logging-statements) section.\n\nExample verbose logging statement with both versions of the logging function:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(\n\t\"Log message\", // message\n\t{ data: \"test\" }, // additionalData\n\t{ [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" }, // customProperties\n\tcleLogger.componentNames.SESSION, // componentName\n\t70123, // logCode\n\t\"my-token\", // correlationToken\n\ttrue, // containsPII\n);\n\ncleLogger.v2.verbose(\n\t\"Log message\",\n\t{\n\t\tadditionalData: { data: \"test\" },\n\t\tcustomProperties: { [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" },\n\t\tcomponentName: cleLogger.componentNames.SESSION,\n\t\tlogCode: 70123,\n\t\tcorrelationToken: \"my-token\",\n\t\tcontainsPII: true,\n\t}\n);\n```\n\nAs you can see in the examples above, both functions take in the same values, but the `v2` version makes it a bit easier to see which value corresponds to each key. It comes down to personal preference, use whichever you prefer.\n\n### Logging constants\n\n**Logging levels**\n\nLogging level constants should be used when configuring the log level of a [log sink](#log-sinks).\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.logLevels.VERBOSE; // verbose\ncleLogger.logLevels.INFO; // info\ncleLogger.logLevels.WARN; // warn\ncleLogger.logLevels.ERROR; // error\ncleLogger.logLevels.CRITICAL; // critical\n```\n\n**Component names**\n\nDefines a set of commonly used components that should be used for the `componentName` parameter value of a log statement if you are logging from one of these common components in a Twilio implementation.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.componentNames.SESSION;\ncleLogger.componentNames.DIALER;\ncleLogger.componentNames.DASHBOARD;\ncleLogger.componentNames.AFTERCALL;\ncleLogger.componentNames.SUPERVISOR;\n```\n\n**Custom property keys**\n\nDefines a set of commonly used key that should be used when defining the custom properties object that is supplied in the `customProperties` parameter of any logging statement.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.customPropertyKeys.CHANNEL_SID;\ncleLogger.customPropertyKeys.TASK_SID;\ncleLogger.customPropertyKeys.CALL_SID;\ncleLogger.customPropertyKeys.WORKFLOW_SID;\ncleLogger.customPropertyKeys.WORKER_SID;\n```\n\n### Log codes\n\nIt can be helpful when investigating issues to only view logs related to specific areas of a system. Use Log Codes to give a category definition for what area of the contact center the log activity occurred within. Use numbers within the following pre-defined log code ranges or define custom log codes by using your own numbers higher than 300,000.\n\n- 100 000 - 109 999 => CALL_STATUS\n- 110 000 - 119 999 => IVR\n- 120 000 - 129 999 => ROUTING\n- 130 000 - 139 999 => AGENT_ACTION\n- 140 000 - 149 999 => TRANSFER\n- 150 000 - 159 999 => RM_INTEGRATION\n- 160 000 - 169 999 => WFM_INTEGRATION\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\n\n### Registering Twilio Actions\n\nIt is important to log information from the Twilio Flex UI for all implementations even when there is little customization happening. Many developers find it useful to generate log activity when some, or any, of the Twilio Actions occur within the Twilio Flex UI. Twilio Actions are events that arise from the Twilio Flex UI base implementation and indicate when certain activity is occurring - such as an agent hanging up a call. The CLE Logger library can be setup to automatically register all, or selected, Twilio Actions such that right after any code tied to the Action event has been executed a log statement is written indicating the Action occurred. This log statement will also automatically include the action event payload. To enable this logging capability, use the following approach to register the Twilio Actions. This only needs to be performed once during the initialization of the Twilio Flex UI page on the client.\n\nExample in a Twilio Flex UI plugin:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n/**\n * @param flex { typeof import('@twilio/flex-ui') }\n * @param manager { import('@twilio/flex-ui').Manager }\n */\ninit(flex, manager) {\n\tcleLogger.init({\n\t\taccountSid: \"your-account-sid\"\n\t});\n\n\tcleLogger.registerTwilioActions(flex);\n}\n```\n\n**Note**: A Twilio account sid must be provided during `init` with the `accountSid` parameter, otherwise the actions won't be logged.\n\nThe logger will loop through all actions found under `flex.Actions.actions`, and attach `after` event handlers to log the action name and payload. Any private properties found in the payload (starting with '\\_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Not all Action events need to be logged and by using the `excludedFlexActionNames` property in the logger configuration the Action events not desired can be ignored. Any actions names configured in `excludedFlexActionNames` will not be included in the logs.\n\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\n\n**Twilio Actions log levels**\n\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of `INFO`.\n\n```js\nAcceptTask: INFO;\nCancelTransfer: INFO;\nCompleteTask: INFO;\nHangupCall: INFO;\nHideDirectory: VERBOSE;\nHistoryGo: VERBOSE;\nHistoryGoBack: VERBOSE;\nHistoryGoForward: VERBOSE;\nHistoryPush: VERBOSE;\nHistoryReplace: VERBOSE;\nHoldCall: INFO;\nHoldParticipant: INFO;\nKickParticipant: INFO;\nLogout: INFO;\nMonitorCall: INFO;\nNavigateToView: INFO;\nRejectTask: INFO;\nSelectTask: INFO;\nSelectTaskInSupervisor: INFO;\nSelectWorkerInSupervisor: INFO;\nSendMessage: INFO;\nSendTyping: VERBOSE;\nSetActivity: INFO;\nSetInputText: VERBOSE;\nShowDirectory: VERBOSE;\nStopMonitoringCall: INFO;\nToggleMute: INFO;\nToggleSidebar: VERBOSE;\nTransferTask: INFO;\nUnholdCall: INFO;\nUnholdParticipant: INFO;\nWrapupTask: INFO;\n```\n\n### Twilio Event Hooks\n\nCurrently, the Twilio console only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with `twilioEventHooks` option, and use the following functions to send event data to a custom endpoint.\n\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is `application/x-www-form-urlencoded` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// unrelated init properties omitted from this example.\n\t// include in the twilioEventHooks array an entry for only the event hooks\n\t// the executing code is going to relay. For example, if the code is only\n\t// handling task router web hook events then you would only need to define\n\t// the taskRouter relay url here.\n\ttwilioEventHooks: {\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\n\t},\n});\n\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\n```\n\nExample Azure Function hook for debugger events:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\nmodule.exports = async function(context, req) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\t},\n\t});\n\n\t// log to console\n\tcleLogger.info(\"Received Twilio debugger event\");\n\n\t// send event data to a configured url\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\n};\n```\n\n**NOTE WHEN USING THIS FUNCTIONALITY IN TWILIO FUNCTIONS:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to `x-www-form-urlencoded` format.\n\nExample Twilio function task router relay using the `form-urlencoded` npm package to convert the data to `x-www-form-urlencoded` format:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\nconst formurlencoded = require(\"form-urlencoded\").default;\n\nexports.handler = async function(context, event, callback) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\t},\n\t});\n\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\n\n\tcallback();\n};\n```\n\n### Version\n\nVersion will be automatically included in all logs.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.version; // 1.0.0\n```\n\n### Error To Object\n\nUtility function to convert an `Error` object to a plain JS object, with all properties preserved. When serialized, `Error` objects will omit custom properties from the output which makes debugging difficult. This utility will traverse all properties in the `Error` object, and output a plain JS object with all properties including the stack trace. See an example below.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\nconst err = new Error(\"error message\");\nconsole.log(err); // \"Error: error message\\n    at Object.<anonymous> …\"\n\nconst errObject = cleLogger.errorToObject(err);\nconsole.log(errObjct); // {name: 'Error', message: 'error message', stack: 'Error: error message\\n    at Object.<anonymous> …'}\n```\n\nExample usage when logging errors:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ntry {\n\t// some code that can throw an error\n} catch (error) {\n\tcleLogger.error(\"My error message\", cleLogger.errorToObject(error));\n}\n```\n\n## Log Object\n\nThe following is the type definition of the log object received by [log sinks](#log-sinks). It consists of properties provided in log messages, as well as some that are automatically inserted like the `eventDate`. You can use this information to customize your logs inside a custom log sink (e.g. filter out unwanted logs, modify some of the properties to include more data, or any kind of conditional logic based on the value of these properties). If using the built in `remoteLogger` log processor with your custom backend, you can expect an array of these objects to be sent to your log endpoint. For more info about implementing a custom logging endpoint see the [remote logging](#remote-logging) section.\n\n```js\n{\n\t// Account sid if provided during init\n\taccountSid: string;\n\n\t// Any additional log data to be included with the log\n\tadditionalData: unknown;\n\n\t// Component name, if provided\n\tcomponentName: string;\n\n\t// Generated or configured token\n\tcorrelationToken: string;\n\n\t// Current UTC datetime in ISO 8601 format\n\teventDate: string;\n\n\t// Log level\n\tlevel: string;\n\n\t// Log code, if provided\n\tlogCode: number;\n\n\t// Log message\n\tmessage: string;\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\tproperties: object;\n\n\t// BrowserFunction if the environment is the browser\n\t// CloudFunction if the environment is Node.js\n\tsource: string;\n\n\t// Indicates that the log contains PII data\n\tcontainsPII: boolean;\n\n\t// Logging library version\n\tloggerVersion: string;\n}\n```\n\n## Handling PII Data\n\nThe logger provides support for helping to protect log messages or data that need to include PII (Personally Identifiable Information). While the logger has several mechanisms that can be used to flag logs that contain PII and block PII from going to particular log sinks, it is still ultimately the developers responsibility to ensure PII is flagged or marked appropriately for the logger to know when it exists - the logger cannot automatically detect if PII is part of a log statement or data object. The general guideline is that developers should avoid including any PII data in log information whenever possible. In the instances where PII data must be included there are several mechanisms the logger supports to help indicate PII data and protect where it is sent.\n\nA `containsPII` parameter exists on log statement methods. This flag should be set to true if there is PII data, or any potential chance of it, in the log message, additional data or custom properties data. When this flag is set to true then it will only allow log sinks that have the `piiSafe` configuration set to true to receive and process the log message. `piiSafe` indicates on a log sink whether that log sink receiver is able to internally store PII data OR has logic to strip PII data that is tagged in the message or from pre-configured additional data fields before the log is written to any storage or output device. When sending data to any `remoteLogger` endpoint the log sink can be marked `piiSafe` only as long as data is appropriately tagged in messages and/or the names of custom properties that might have PII values in additional data are pre-configured AND the endpoint implementer has verified and ensured their receiving code will remove marked PII data immediately before sending logs to any output or storage media.\n\nTo tag any part of the message parameter in a log statement as containing PII within the actual log message use the `tagPII` utility. The `tagPII` utility will indicate to a remote backend that the text between the tags should be stripped or masked before sending logs to any output or storage media.\n\nExample tagged output:\n\n```js\nimport { tagPII } from \"@prftesp/cle-logger\";\n\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\n```\n\n## Advanced Configuration\n\n### Remote logging\n\nThe library provides a built in way to transmit logs to a remote http endpoint using the `remoteLogger` logs processor. The `remoteLogger` can be directed at a known listener such as CLE or you can create your own custom log listener. In addition, the log level in effect can be controlled remotely setting the `minLogLevelOverrideUrl` to an http address that will return the current log level to use instead of any defined default logLevel on each `logSink`. The `minLogLevelOverrideUrl` can point to a known central API such as CLE or you can create your own endpoint.\n\nThe following sections outline how to create your own endpoints for either receiving log messages from the `remoteLogger` log sink or providing the current log level override to it.\n\n**Remote log endpoint**\n\nTo implement your own remote logging endpoint, all you have to do is accept `POST` requests, with a JSON payload of an array of [log objects](#log-object), and return either `{ success: true }` or `{ success: false }`. The endpoint should always return a `200 OK` status even if your server code fails so that the CLE logger does not cause failures in the business logic in which it is embedded. A good use-case would be leveraging a serverless api platform to output logs to a queue.\n\nExample of an Azure Function that can receive requests from the `remoteLogger` log sink:\n\n```js\nmodule.exports = async function(context, req) {\n\tcontext.log(req.body); // body will have the log array\n\n\tcontext.res = {\n\t\tstatus: 200,\n\t\tbody: { success: true },\n\t};\n};\n```\n\n**Remote log level override**\n\nThe `minLogLevelOverrideUrl` configuration property is used to provide an endpoint that accepts `GET` requests and a `accountSid` query string parameter. `accountSid` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the `minLogLevel` configuration property. If null is returned, the `minLogLevel` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a `200 OK` status code and use the `success` property to indicate failure.\n\nExample success response:\n\n```json\n{\n\t\"success\": true,\n\t\"minLogLevel\": \"warn\"\n}\n```\n\nExample error response:\n\n```json\n{\n\t\"success\": false,\n\t\"minLogLevel\": null\n}\n```\n\n## Changelog\n\n### v2.2.0\n\n**New features**\n\n- Added [alternative logging functions](#alternative-logging-functions) that have a much shorter parameter list, and allow you to specify only those logging properties that you want, without the need to pass in `null` for parameters you are not interested in. New functions are available under `cleLogger.v2` for backward compatibility reasons.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n// if you want to log just the message and the token, \n// you need to default parameters to null until you get to token\ncleLogger.verbose(\"Log message\", null, null, null, null, \"my-token\");\n\n// with the new functions, you just provide an object with the properties you want\ncleLogger.v2.verbose(\"Log message\", { correlationToken: \"my-token\" });\n```\n\n### v2.1.0\n\n**New features**\n\n- Added [Error To Object](#error-to-object) method to assist users in converting error object to plain objects.\n\n### v2.0.7\n\n**New features**\n\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the call sid during a call, and automatically include it in `customProperties` log property for all logs that happen during that call (including Flex Action logs).\n\n### v2.0.5\n\n**New features**\n\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the worker sid and automatically include it in `customProperties` log property.\n\n**Other updates**\n\n- Updated documentation based on user feedback.\n\n### v2.0.4\n\n**Bug fixes**\n\n- Fixed issue with logging to console in Twilio Functions. The `consoleLogger` will now output the log object as serialized json.\n\n### v2.0.0 [2019-12-20]\n\n**New features**\n\n- All logging output is now configured using the `logSinks` property. The library provides two commonly used loggers - `consoleLogger` and a `remoteLogger` that accepts the log url as a parameter. [Configuration](#configuration)\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\n- Log functions have a new optional `containsPII` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\n- A new helper function `tagPII` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\n\n**Breaking changes**\n\n- `customLogSink` configuration renamed to `logSinks`\n- Removed `disableRemoteLogging` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\n- Removed `logToConsole` configuration. This is now configured as a log sink with a `consoleLogger` helper function.\n- Removed `logUrl` configuration. This is now configured as a log sink with a `remoteLogger` helper function.\n- Removed `minLogLevel` configuration. This is now configured per log sink.\n\n**Migration guide**\n\n```js\nimport cleLogger from \"@prftesp/cle-logger\";\n\n// v1.0.0 and older config\ncleLogger.init({\n\tdisableRemoteLogSinks: true, // no direct replacement\n\tlogToConsole: true, // replaced by the \"console logger\" sink\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\n\tcustomLogSinks: [\n\t\t{\n\t\t\tname: \"custom sink\",\n\t\t\tlog: logs => {\n\t\t\t\t/* your custom logging */\n\t\t\t},\n\t\t},\n\t],\n});\n\n// v2.0.0 config\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"custom sink\",\n\t\t\tlog: logs => {\n\t\t\t\t/* your custom logging */\n\t\t\t},\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"console logger\",\n\t\t\tlog: consoleLogger,\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"remote logger\",\n\t\t\tlog: remoteLogger(\"http://log.output/api\"),\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t],\n});\n```\n\n### v1.0.0 [2019-09-12]\n\n- Initial release\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.2.0-beta.20200618.4","_nodeVersion":"12.18.0","_npmVersion":"6.14.5","dist":{"integrity":"sha512-YoH5i6yNKU2aSJXqf4jaceWDuz65dewbtJSY/herFg738KeBtddAt/mj/YIiYslKYdzg3l17woREeKbpfRYhgQ==","shasum":"7a003b4b5d60d1d82d93ddf4643f36d199c8283b","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.2.0-beta.20200618.4.tgz","fileCount":17,"unpackedSize":87526,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe66NVCRA9TVsSAnZWagAAZ8sP+wVdvBFZBhyGzHhzEavU\n2Cq9kjI32VRnvVRx1vlAu2okz+KKisGPzJ0lhWwPxbISSjS0gU3EsEjUJzOo\n7aQ87+i7OBUjVecydwTKJD6EtNh3XGAWUI4tnMcNzVNEjf8Rd6iQe/8j6d8A\nPMZKq2ykSpICNfAo4Ij5ue5Djns8BAhOWsIdkYeLYZAjwOAESwKiFnVZQgBa\np1dKfjHpxJdC2wIxceGbczME13OoFT8z/c7mvPw46Yy2y7cx/314T+PJWLwo\nccRrEwLjSS/z4XgfUz70dwplinD4RRMHAOjzLHDr2rCSxqMK7EFT/b8CzXAO\nFyzokVQlpI2wSYSdabrY9djGxJ5+C21pZvzV960wanKiFFJssI6pcVuB9TOw\nuz4VFprjSMOO3gDZv2GaBJ9My3eXFrX86HTn5a/vYeiYrjlu9wB9JIluWrzE\nzYQuHjaOZH/NYOr6TMFHwBW7ThbnTfADlxWyAbIqUINkoyJjr0ec5GKhNTp1\ngAlk/baZoeivAVFh7s1vsBlSPzitXBCUS2TbJ9ZVSmBnoMb39ySzXy/67YlN\nuSUWLigY8TQs6ao6iFqRVWS7b+NIL8iMiZXNmeCj82TpFQ8tZ4UqtBXtbBNq\n+LOCamZEDuNRBwh38+IkCYNPY4incxcf3y1HrtHFA6sRJrv+mLLqIXc1Dx2W\n4IM9\r\n=HD4q\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCQUtREtF483mIH7XKivqGBYqyl6NVH19PTpf4o6TaHPgIhAJdXcK1ondj4ID8A9hawQfOxP30nNvc/5Uyd2GnTaPas"}]},"maintainers":[{"email":"c.swartzentruber@perficient.com","name":"curtis.swartzentruber"},{"email":"darko.majkic@perficient.com","name":"darko.perficient"},{"email":"espnpm@perficient.com","name":"espnpm"},{"email":"mike.frank@perficient.com","name":"mikefrank"},{"email":"shelby.hagman@perficient.com","name":"shelby-prft"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.2.0-beta.20200618.4_1592501076536_0.0544285620594791"},"_hasShrinkwrap":false},"2.2.0":{"name":"@prftesp/cle-logger","version":"2.2.0","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build && npm run build:es5","build:es5":"rimraf dist-es5/* && tsc --build tsconfig.es5.json","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^25.1.4","@types/node":"^12.12.31","@typescript-eslint/eslint-plugin":"^2.25.0","@typescript-eslint/parser":"^2.25.0","eslint":"^6.8.0","eslint-config-prettier":"^6.10.1","eslint-plugin-prettier":"^3.1.2","jest":"^25.2.2","prettier":"^1.19.1","rimraf":"^3.0.2","ts-jest":"^25.2.1","typescript":"^3.8.3"},"dependencies":{"axios":"^0.19.2","serializerr":"^1.0.3"},"_id":"@prftesp/cle-logger@2.2.0","_nodeVersion":"12.18.0","_npmVersion":"6.14.5","dist":{"integrity":"sha512-CCq5FdW1e4XGEQyUqc9N70s/rZ9+h8HNq0+NP3bad7TSp3Xt3UBdb2I5cebA8tnjxaE/btGRxRIUZaVrBRiIkQ==","shasum":"83b8e911c71cd9c41729a60200d5fc27f2374ae3","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.2.0.tgz","fileCount":17,"unpackedSize":87423,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe66TuCRA9TVsSAnZWagAA03YP/2nJeTbGOruvK6m35Qzg\n3IxLTqHhPHUGOah7WjUXij9CwsnfveMnTvgNun/igrT6FG3/T7QfWbWBDt3Y\nqfY+oXQENvMGWMhvfsmyXpBtfGxVDx8aq/1EQOHBUCaczuiFCMRtQvN7zGCH\ni1QqD3NlFi3DkffPo5O4uI9UqmvFkZG9sQLAjcDT/B5Glad+3mk7BKmqDLDn\n5zlv9s9R8kIHe1CMlEquKs9oSt/RMhSDHMU4cj9TOAWXVHQ4pn8Ur8EU/Afh\nNSgSZk5Jdz5gYkreXt8+9NKRautUgnGnXbh8Nv0x+fmrmplAtPHuxdlmaVF1\nXdgk4Fw7/8AhC+mHv6b26H1zmUOcP7MeFRJY5PK1I89CVmwqoTxyCktoqkxX\njAmpPcM8eP+w15XQCbd+ZN2WTv+o4Xdj6LRoXVxcR2A5q7VRLburpn8IrmLz\nE7yhDOPm2yzmCXg8+x/ZtBAmM1BeH7Orv9TiW33xCRwozBmjmKDcvMCiFIO5\nCv+JR51K1Khj9iZSZ8O3qBahR6UQuGEQxPL3F2YG2a4Dc7grL0F0MV2Ud1k9\nKHb7Rw7zEvgoDwbC+vrd5OBW5pa/YRrVPEhLg5IWVQj5JYfVhDTK2IRlMkGO\ndM3AzlXtgiIqPUzyvSKZpef0vRI6ekGlIwSpOfZCcSvnBnoxDkmR2QPoTfwY\nEuMf\r\n=/OaF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIB5H2RZYt/L13yVFtOol1hR8nmEC2wCBoZ7djuQVq1PMAiBnPvLiEXX9Fs3uI5ohKNlsgE0ZZvJ4rpRpuSJraB8w9w=="}]},"maintainers":[{"email":"c.swartzentruber@perficient.com","name":"curtis.swartzentruber"},{"email":"darko.majkic@perficient.com","name":"darko.perficient"},{"email":"espnpm@perficient.com","name":"espnpm"},{"email":"mike.frank@perficient.com","name":"mikefrank"},{"email":"shelby.hagman@perficient.com","name":"shelby-prft"}],"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.2.0_1592501485624_0.48689234294580475"},"_hasShrinkwrap":false},"2.2.0-beta.20201202.2":{"name":"@prftesp/cle-logger","version":"2.2.0-beta.20201202.2","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* && tsc --build && npm run build:es5","build:es5":"rimraf dist-es5/* && tsc --build tsconfig.es5.json","test":"jest --runInBand --config jest.config.json","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^25.1.4","@types/node":"^12.12.31","@typescript-eslint/eslint-plugin":"^2.25.0","@typescript-eslint/parser":"^2.25.0","eslint":"^6.8.0","eslint-config-prettier":"^6.10.1","eslint-plugin-prettier":"^3.1.2","jest":"^25.2.2","prettier":"^1.19.1","rimraf":"^3.0.2","ts-jest":"^25.2.1","typescript":"^3.8.3"},"dependencies":{"axios":"^0.19.2","serializerr":"^1.0.3"},"readme":"﻿CLE Logger\n==\n\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\n\n## Documentation\n\n- [Target Environment](#target-environment)\n- [Simple Example](#simple-example)\n- [Installation](#installation)\n- [Features](#features)\n- [Configuration](#configuration)\n- [API](#api)\n  - [Log sinks](#log-sinks)\n  - [Logging statements](#logging-statements)\n  - [Logging parameters](#logging-parameters)\n  - [Alternative logging functions](#alternative-logging-functions)\n  - [Logging constants](#logging-constants)\n  - [Log codes](#log-codes)\n  - [Registering Twilio Actions](#registering-twilio-actions)\n  - [Twilio Event Hooks](#twilio-event-hooks)\n  - [Version](#version)\n  - [Error To Object](#error-to-object)\n- [Log Object](#log-object)\n- [Handling PII Data](#handling-pii-data)\n- [Advanced Configuration](#advanced-configuration)\n  - [Remote logging](#remote-logging)\n- [Changelog](#changelog)\n\n## Target Environment\n\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\n\n### ES5 Compatible Version\n\nIf you need an ES5 compatible version (for example you need IE11 support), use this package: https://www.npmjs.com/package/@prftesp/cle-logger-es5. **This package is not regularly released with the main library, and should be used only when absolutely necessary**. If you need to support IE11, don't forget to include the necessary polyfills, mainly for `Promise` support. To do this, you can include this script tag on the page, before you load this library `<script src=\"https://cdn.polyfill.io/v3/polyfill.min.js?features=es6\"></script>`, or visit `https://polyfill.io` for more options.\n\n**Please note** that the ES5 version is generated, and is sharing the same code and documentation as the main library. This is why the rest of the documentation doesn't mention the ES5 version in text or examples. The only difference is how you reference it (`@prftesp/cle-logger` vs `@prftesp/cle-logger-es5`). To install it use `npm install @prftesp/cle-logger-es5`, and to import it use `import { cleLogger } from \"@prftesp/cle-logger-es5\";`.\n\n## Simple Example\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"console sink\",\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\tpiiSafe: true,\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n});\n\n// will log message to console\ncleLogger.info(\"log message\");\n```\n\n## Installation\n\n```bash\n$ npm install @prftesp/cle-logger\n```\n\n## Features\n\n- Runs in Node.js and the browser\n- Remote logging\n- Multiple log sinks\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\n- Remote minimum log level overrides\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\n- Support for tagging logs with custom component names or correlation tokens\n- Send Twilio event hook data to a custom backend\n\n## Configuration\n\nTo configure the logger call the `init` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\n\n**Available options**\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// Twilio account sid, required for use with CLE\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\n\n\t// Will be associated with all logs,\n\t// if not provided, a unique uuid will be generated\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\n\n\t// An array of that will receive an array of log objects,\n\t// and can optionally return a promise. Please see the\n\t// \"Log Sinks\" section for more information.\n\tlogSinks: [\n\t\t{\n\t\t\t// name of the log sink\n\t\t\tname: \"exampleConsoleSink\",\n\t\t\t// log handler function\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\t// This flag defines if the log message is processed by this sink when the contains\n\t\t\t// PII parameter of the log statement is true. piiSafe of true indicates all log\n\t\t\t// messages are allowed (even when contains PII is true); piiSafe of false indicates\n\t\t\t// only log messages with contains PII set to false will be processed by this log sink.\n\t\t\t// Defaults to false\n\t\t\tpiiSafe: true,\n\t\t\t// log level for the sink, defaults to INFO\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n\n\t// A way to suppress error messages if set to false, defaults to true\n\tlogErrorsToConsole: true,\n\n\t// If logging Twilio Flex Actions, provide a list of actions\n\t// to be excluded from logs\n\texcludedFlexActionNames: [\"SendTyping\"],\n\n\t// A custom endpoint to fetch log level overrides\n\t// NOTE: the provided url should always end with 'accountSid=',\n\t// the library will append the accountSid\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\n\n\t// An instance of ('@twilio/flex-ui').Manager.\n\t// If provided, it will be used to get the worker sid and call sid (only during a call), and\n\t// automatically include it in 'customProperties' log property and with every log\n\ttwilioFlexManager: manager,\n\n\t// To send data from Twilio event hooks, to a custom backend,\n\t// configure one or more endpoints for each event type\n\ttwilioEventHooks: {\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\n\t},\n});\n```\n\n## API\n\nThe logger can be imported as an object in CommonJs or ES6 syntax:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n// or\nconst cleLogger = require(\"@prftesp/cle-logger\");\n```\n\nAlternatively, all top level members of the `cleLogger` are available as named exports and can be imported separately:\n\n```js\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\n\ninit({\n\tlogSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: logLevels.VERBOSE\n\t}],\n});\n\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\n\n// vs\n\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ninit({\n\tcleLogger.logSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: cleLogger.consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}],\n});\n\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\n```\n\n### Log sinks\n\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the `logSinks` config. The logSinks configuration defines an array of objects with each object defining the log sink activity. All provided log sinks will be executed in parallel. A log sink configuration must include the following properties:\n\n- `name`: this is an identifiable name for the logger and must be unique per log sink defined\n- `log`: this indicates the logic to process on each batch of logs processed by the sink. There are several built in processing functions that can be used or you can insert any code inline with this definition. The built in processing functions are `consoleLogger` which will send the log statements to the attached console or `remoteLogger` which will send the log statements to the specified url provided as a definition on the parameter (i.e. `remoteLogger(\"https://mylogger.url\"`). For a custom log processing implementation, the logic must be async operations and must return a Promise object. The custom log method will receive an array of log objects for you to handle. You can find out more about the log object type in the [Log Object](#log-object) section. Here is an example of a custom log function:\n\n```js\nlogs => {\n\t// log each received log to the console separately\n\tlogs.forEach(log => console.log(log));\n\treturn Promise.resolve();\n};\n```\n\n- `logLevel`: defines the minimum default log level that will be processed by this log sink. Log levels lower than the minimum level will not be processed by this log sink. The minimum default log level can be overridden at run time by the value returned from the overall `minLogLevelOverrideUrl` that is set up in the library `init`.\n- `piiSafe`: indicates if log messages with a `containsPII` parameter value of true should be processed by the log sink. If `containsPII` parameter value on the log message is true and piiSafe is false then the log message will not be processed by this log sink, if piiSafe is true then it will. If the `containsPII` parameter value is false on the log message than it is always processed by the log sink regardless of the piiSafe value.\n\nCustom log sink configuration example. In this example, the custom code executed for each log statement is to append the log into a file. Note how a Promise is returned as required:\n\n```js\n{\n\t// name of the log sink\n\tname: \"exampleConsoleSink\",\n\t// log handler function - this is where you put your custom handling code and\n\t// remember to always return a promise. If your operation is not asynchronous,\n\t// you can do this by returning Promise.resolve().\n\tlog: logs => {\n\t\tfs.appendFile(\n\t\t\t\"./log.txt\",\n\t\t\t`${JSON.stringify(logs)}`,\n\t\t\tconsole.log\n\t\t);\n\t\treturn Promise.resolve();\n\t},\n\t// should this log sink accept PII data, defaults to false\n\tpiiSafe: true,\n\t// log level for the sink, defaults to INFO\n\tlogLevel: cleLogger.logLevels.VERBOSE\n}\n```\n\nExample log sink configuration that uses a custom coded sink and both standard console and remote logging sinks:\n\n```js\nconst fs = require(\"fs\");\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"fileSink\",\n\t\t\tlog: logs => {\n\t\t\t\tfs.appendFile(\"./log.txt\", `${JSON.stringify(logs)}`, console.log);\n\t\t\t\treturn Promise.resolve();\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"remoteSink\",\n\t\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\n\t\t\tlogLevel: cleLogger.logLevels.WARN,\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"consoleSink\",\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n});\n```\n\n### Logging statements\n\nTo create logs, you can use one of the logging function available. They all take the same arguments, described below, and log at a corresponding log level. For example, if you use the `cleLogger.verbose` logging function, and your log sink is configured with `logLevel: cleLogger.logLevels.VERBOSE`, that log sink will receive the log. If your log sink is configured with a higher log level, for example `logLevel: cleLogger.logLevels.INFO`, then it would not receive the verbose log.\n\nHere are the available logging functions:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(...);\ncleLogger.info(...);\ncleLogger.warn(...);\ncleLogger.error(...);\ncleLogger.critical(...);\n```\n\nThe functions are all async, return `Promise<void>`, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\n\n### Logging parameters\n\nAll logging functions take the same parameters:\n\n- `message`: the only required property, no logs will be generated without it\n  - type: string\n  - required: yes\n- `additionalData`: any additional log data you would like to include with the log, can be any data type\n  - type: any\n  - required: no\n  - default value: null\n- `customProperties`: a single level deep object of important properties you would like to distinguish from general log data, like various sids CLE uses it to index these properties in Application Insights. For CLE, try to use keys found in `cleLogger.customPropertyKeys`\n  - type: object\n  - required: no\n  - default value: {}\n- `componentName`: another useful way to 'tag' your logs for easier searching. Can be any string, but for CLE, try to use available options in `cleLogger.componentNames`\n  - type: string\n  - required: no\n  - default value: \"\"\n- `logCode`: log code, can be any number used to categorize logs. For defined ranges see the [log codes](#log-codes) section.\n  - type: number\n  - required: no\n  - default value: 0\n- `correlationToken`: correlation token to be associated with this log only\n  - type: string\n  - required: no\n  - default value: token provided in `init`, or a generated token if one wasn't provided\n- `containsPII`: does the log contain PII data, defaults to false. If set to true, the log will be passed in only to those log sinks that have `piiSafe` set to true. For more info see the [handling PII data](#handling-pii-data) section.\n  - type: boolean\n  - required: no\n  - default value: false\n\n### Alternative logging functions\n\nIf you would prefer to pass in an object to logging functions, instead of using a long list of parameters, you can use the new logging functions available under the `cleLogger.v2` namespace. These functions are namespaced to keep backwards compatibility, but you should feel free to use them going forward. The behavior of these functions is exactly the same as with those listed in [logging statements](#logging-statements) section.\n\nExample verbose logging statement with both versions of the logging function:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(\n\t\"Log message\", // message\n\t{ data: \"test\" }, // additionalData\n\t{ [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" }, // customProperties\n\tcleLogger.componentNames.SESSION, // componentName\n\t70123, // logCode\n\t\"my-token\", // correlationToken\n\ttrue, // containsPII\n);\n\ncleLogger.v2.verbose(\n\t\"Log message\",\n\t{\n\t\tadditionalData: { data: \"test\" },\n\t\tcustomProperties: { [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" },\n\t\tcomponentName: cleLogger.componentNames.SESSION,\n\t\tlogCode: 70123,\n\t\tcorrelationToken: \"my-token\",\n\t\tcontainsPII: true,\n\t}\n);\n```\n\nAs you can see in the examples above, both functions take in the same values, but the `v2` version makes it a bit easier to see which value corresponds to each key. It comes down to personal preference, use whichever you prefer.\n\n### Logging constants\n\n**Logging levels**\n\nLogging level constants should be used when configuring the log level of a [log sink](#log-sinks).\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.logLevels.VERBOSE; // verbose\ncleLogger.logLevels.INFO; // info\ncleLogger.logLevels.WARN; // warn\ncleLogger.logLevels.ERROR; // error\ncleLogger.logLevels.CRITICAL; // critical\n```\n\n**Component names**\n\nDefines a set of commonly used components that should be used for the `componentName` parameter value of a log statement if you are logging from one of these common components in a Twilio implementation.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.componentNames.SESSION;\ncleLogger.componentNames.DIALER;\ncleLogger.componentNames.DASHBOARD;\ncleLogger.componentNames.AFTERCALL;\ncleLogger.componentNames.SUPERVISOR;\n```\n\n**Custom property keys**\n\nDefines a set of commonly used key that should be used when defining the custom properties object that is supplied in the `customProperties` parameter of any logging statement.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.customPropertyKeys.CHANNEL_SID;\ncleLogger.customPropertyKeys.TASK_SID;\ncleLogger.customPropertyKeys.CALL_SID;\ncleLogger.customPropertyKeys.WORKFLOW_SID;\ncleLogger.customPropertyKeys.WORKER_SID;\n```\n\n### Log codes\n\nIt can be helpful when investigating issues to only view logs related to specific areas of a system. Use Log Codes to give a category definition for what area of the contact center the log activity occurred within. Use numbers within the following pre-defined log code ranges or define custom log codes by using your own numbers higher than 300,000.\n\n- 100 000 - 109 999 => CALL_STATUS\n- 110 000 - 119 999 => IVR\n- 120 000 - 129 999 => ROUTING\n- 130 000 - 139 999 => AGENT_ACTION\n- 140 000 - 149 999 => TRANSFER\n- 150 000 - 159 999 => RM_INTEGRATION\n- 160 000 - 169 999 => WFM_INTEGRATION\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\n\n### Registering Twilio Actions\n\nIt is important to log information from the Twilio Flex UI for all implementations even when there is little customization happening. Many developers find it useful to generate log activity when some, or any, of the Twilio Actions occur within the Twilio Flex UI. Twilio Actions are events that arise from the Twilio Flex UI base implementation and indicate when certain activity is occurring - such as an agent hanging up a call. The CLE Logger library can be setup to automatically register all, or selected, Twilio Actions such that right after any code tied to the Action event has been executed a log statement is written indicating the Action occurred. This log statement will also automatically include the action event payload. To enable this logging capability, use the following approach to register the Twilio Actions. This only needs to be performed once during the initialization of the Twilio Flex UI page on the client.\n\nExample in a Twilio Flex UI plugin:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n/**\n * @param flex { typeof import('@twilio/flex-ui') }\n * @param manager { import('@twilio/flex-ui').Manager }\n */\ninit(flex, manager) {\n\tcleLogger.init({\n\t\taccountSid: \"your-account-sid\"\n\t});\n\n\tcleLogger.registerTwilioActions(flex);\n}\n```\n\n**Note**: A Twilio account sid must be provided during `init` with the `accountSid` parameter, otherwise the actions won't be logged.\n\nThe logger will loop through all actions found under `flex.Actions.actions`, and attach `after` event handlers to log the action name and payload. Any private properties found in the payload (starting with '\\_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Not all Action events need to be logged and by using the `excludedFlexActionNames` property in the logger configuration the Action events not desired can be ignored. Any actions names configured in `excludedFlexActionNames` will not be included in the logs.\n\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\n\n**Twilio Actions log levels**\n\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of `INFO`.\n\n```js\nAcceptTask: INFO;\nCancelTransfer: INFO;\nCompleteTask: INFO;\nHangupCall: INFO;\nHideDirectory: VERBOSE;\nHistoryGo: VERBOSE;\nHistoryGoBack: VERBOSE;\nHistoryGoForward: VERBOSE;\nHistoryPush: VERBOSE;\nHistoryReplace: VERBOSE;\nHoldCall: INFO;\nHoldParticipant: INFO;\nKickParticipant: INFO;\nLogout: INFO;\nMonitorCall: INFO;\nNavigateToView: INFO;\nRejectTask: INFO;\nSelectTask: INFO;\nSelectTaskInSupervisor: INFO;\nSelectWorkerInSupervisor: INFO;\nSendMessage: INFO;\nSendTyping: VERBOSE;\nSetActivity: INFO;\nSetInputText: VERBOSE;\nShowDirectory: VERBOSE;\nStopMonitoringCall: INFO;\nToggleMute: INFO;\nToggleSidebar: VERBOSE;\nTransferTask: INFO;\nUnholdCall: INFO;\nUnholdParticipant: INFO;\nWrapupTask: INFO;\n```\n\n### Twilio Event Hooks\n\nCurrently, the Twilio console only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with `twilioEventHooks` option, and use the following functions to send event data to a custom endpoint.\n\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is `application/x-www-form-urlencoded` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// unrelated init properties omitted from this example.\n\t// include in the twilioEventHooks array an entry for only the event hooks\n\t// the executing code is going to relay. For example, if the code is only\n\t// handling task router web hook events then you would only need to define\n\t// the taskRouter relay url here.\n\ttwilioEventHooks: {\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\n\t},\n});\n\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\n```\n\nExample Azure Function hook for debugger events:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\nmodule.exports = async function(context, req) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\t},\n\t});\n\n\t// log to console\n\tcleLogger.info(\"Received Twilio debugger event\");\n\n\t// send event data to a configured url\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\n};\n```\n\n**NOTE WHEN USING THIS FUNCTIONALITY IN TWILIO FUNCTIONS:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to `x-www-form-urlencoded` format.\n\nExample Twilio function task router relay using the `form-urlencoded` npm package to convert the data to `x-www-form-urlencoded` format:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\nconst formurlencoded = require(\"form-urlencoded\").default;\n\nexports.handler = async function(context, event, callback) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\t},\n\t});\n\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\n\n\tcallback();\n};\n```\n\n### Version\n\nVersion will be automatically included in all logs.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.version; // 1.0.0\n```\n\n### Error To Object\n\nUtility function to convert an `Error` object to a plain JS object, with all properties preserved. When serialized, `Error` objects will omit custom properties from the output which makes debugging difficult. This utility will traverse all properties in the `Error` object, and output a plain JS object with all properties including the stack trace. See an example below.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\nconst err = new Error(\"error message\");\nconsole.log(err); // \"Error: error message\\n    at Object.<anonymous> …\"\n\nconst errObject = cleLogger.errorToObject(err);\nconsole.log(errObjct); // {name: 'Error', message: 'error message', stack: 'Error: error message\\n    at Object.<anonymous> …'}\n```\n\nExample usage when logging errors:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ntry {\n\t// some code that can throw an error\n} catch (error) {\n\tcleLogger.error(\"My error message\", cleLogger.errorToObject(error));\n}\n```\n\n## Log Object\n\nThe following is the type definition of the log object received by [log sinks](#log-sinks). It consists of properties provided in log messages, as well as some that are automatically inserted like the `eventDate`. You can use this information to customize your logs inside a custom log sink (e.g. filter out unwanted logs, modify some of the properties to include more data, or any kind of conditional logic based on the value of these properties). If using the built in `remoteLogger` log processor with your custom backend, you can expect an array of these objects to be sent to your log endpoint. For more info about implementing a custom logging endpoint see the [remote logging](#remote-logging) section.\n\n```js\n{\n\t// Account sid if provided during init\n\taccountSid: string;\n\n\t// Any additional log data to be included with the log\n\tadditionalData: unknown;\n\n\t// Component name, if provided\n\tcomponentName: string;\n\n\t// Generated or configured token\n\tcorrelationToken: string;\n\n\t// Current UTC datetime in ISO 8601 format\n\teventDate: string;\n\n\t// Log level\n\tlevel: string;\n\n\t// Log code, if provided\n\tlogCode: number;\n\n\t// Log message\n\tmessage: string;\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\tproperties: object;\n\n\t// BrowserFunction if the environment is the browser\n\t// CloudFunction if the environment is Node.js\n\tsource: string;\n\n\t// Indicates that the log contains PII data\n\tcontainsPII: boolean;\n\n\t// Logging library version\n\tloggerVersion: string;\n}\n```\n\n## Handling PII Data\n\nThe logger provides support for helping to protect log messages or data that need to include PII (Personally Identifiable Information). While the logger has several mechanisms that can be used to flag logs that contain PII and block PII from going to particular log sinks, it is still ultimately the developers responsibility to ensure PII is flagged or marked appropriately for the logger to know when it exists - the logger cannot automatically detect if PII is part of a log statement or data object. The general guideline is that developers should avoid including any PII data in log information whenever possible. In the instances where PII data must be included there are several mechanisms the logger supports to help indicate PII data and protect where it is sent.\n\nA `containsPII` parameter exists on log statement methods. This flag should be set to true if there is PII data, or any potential chance of it, in the log message, additional data or custom properties data. When this flag is set to true then it will only allow log sinks that have the `piiSafe` configuration set to true to receive and process the log message. `piiSafe` indicates on a log sink whether that log sink receiver is able to internally store PII data OR has logic to strip PII data that is tagged in the message or from pre-configured additional data fields before the log is written to any storage or output device. When sending data to any `remoteLogger` endpoint the log sink can be marked `piiSafe` only as long as data is appropriately tagged in messages and/or the names of custom properties that might have PII values in additional data are pre-configured AND the endpoint implementer has verified and ensured their receiving code will remove marked PII data immediately before sending logs to any output or storage media.\n\nTo tag any part of the message parameter in a log statement as containing PII within the actual log message use the `tagPII` utility. The `tagPII` utility will indicate to a remote backend that the text between the tags should be stripped or masked before sending logs to any output or storage media.\n\nExample tagged output:\n\n```js\nimport { tagPII } from \"@prftesp/cle-logger\";\n\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\n```\n\n## Advanced Configuration\n\n### Remote logging\n\nThe library provides a built in way to transmit logs to a remote http endpoint using the `remoteLogger` logs processor. The `remoteLogger` can be directed at a known listener such as CLE or you can create your own custom log listener. In addition, the log level in effect can be controlled remotely setting the `minLogLevelOverrideUrl` to an http address that will return the current log level to use instead of any defined default logLevel on each `logSink`. The `minLogLevelOverrideUrl` can point to a known central API such as CLE or you can create your own endpoint.\n\nThe following sections outline how to create your own endpoints for either receiving log messages from the `remoteLogger` log sink or providing the current log level override to it.\n\n**Remote log endpoint**\n\nTo implement your own remote logging endpoint, all you have to do is accept `POST` requests, with a JSON payload of an array of [log objects](#log-object), and return either `{ success: true }` or `{ success: false }`. The endpoint should always return a `200 OK` status even if your server code fails so that the CLE logger does not cause failures in the business logic in which it is embedded. A good use-case would be leveraging a serverless api platform to output logs to a queue.\n\nExample of an Azure Function that can receive requests from the `remoteLogger` log sink:\n\n```js\nmodule.exports = async function(context, req) {\n\tcontext.log(req.body); // body will have the log array\n\n\tcontext.res = {\n\t\tstatus: 200,\n\t\tbody: { success: true },\n\t};\n};\n```\n\n**Remote log level override**\n\nThe `minLogLevelOverrideUrl` configuration property is used to provide an endpoint that accepts `GET` requests and a `accountSid` query string parameter. `accountSid` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the `minLogLevel` configuration property. If null is returned, the `minLogLevel` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a `200 OK` status code and use the `success` property to indicate failure.\n\nExample success response:\n\n```json\n{\n\t\"success\": true,\n\t\"minLogLevel\": \"warn\"\n}\n```\n\nExample error response:\n\n```json\n{\n\t\"success\": false,\n\t\"minLogLevel\": null\n}\n```\n\n## Changelog\n\n### v2.2.0\n\n**New features**\n\n- Added [alternative logging functions](#alternative-logging-functions) that have a much shorter parameter list, and allow you to specify only those logging properties that you want, without the need to pass in `null` for parameters you are not interested in. New functions are available under `cleLogger.v2` for backward compatibility reasons.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n// if you want to log just the message and the token, \n// you need to default parameters to null until you get to token\ncleLogger.verbose(\"Log message\", null, null, null, null, \"my-token\");\n\n// with the new functions, you just provide an object with the properties you want\ncleLogger.v2.verbose(\"Log message\", { correlationToken: \"my-token\" });\n```\n\n### v2.1.0\n\n**New features**\n\n- Added [Error To Object](#error-to-object) method to assist users in converting error object to plain objects.\n\n### v2.0.7\n\n**New features**\n\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the call sid during a call, and automatically include it in `customProperties` log property for all logs that happen during that call (including Flex Action logs).\n\n### v2.0.5\n\n**New features**\n\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the worker sid and automatically include it in `customProperties` log property.\n\n**Other updates**\n\n- Updated documentation based on user feedback.\n\n### v2.0.4\n\n**Bug fixes**\n\n- Fixed issue with logging to console in Twilio Functions. The `consoleLogger` will now output the log object as serialized json.\n\n### v2.0.0 [2019-12-20]\n\n**New features**\n\n- All logging output is now configured using the `logSinks` property. The library provides two commonly used loggers - `consoleLogger` and a `remoteLogger` that accepts the log url as a parameter. [Configuration](#configuration)\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\n- Log functions have a new optional `containsPII` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\n- A new helper function `tagPII` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\n\n**Breaking changes**\n\n- `customLogSink` configuration renamed to `logSinks`\n- Removed `disableRemoteLogging` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\n- Removed `logToConsole` configuration. This is now configured as a log sink with a `consoleLogger` helper function.\n- Removed `logUrl` configuration. This is now configured as a log sink with a `remoteLogger` helper function.\n- Removed `minLogLevel` configuration. This is now configured per log sink.\n\n**Migration guide**\n\n```js\nimport cleLogger from \"@prftesp/cle-logger\";\n\n// v1.0.0 and older config\ncleLogger.init({\n\tdisableRemoteLogSinks: true, // no direct replacement\n\tlogToConsole: true, // replaced by the \"console logger\" sink\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\n\tcustomLogSinks: [\n\t\t{\n\t\t\tname: \"custom sink\",\n\t\t\tlog: logs => {\n\t\t\t\t/* your custom logging */\n\t\t\t},\n\t\t},\n\t],\n});\n\n// v2.0.0 config\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"custom sink\",\n\t\t\tlog: logs => {\n\t\t\t\t/* your custom logging */\n\t\t\t},\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"console logger\",\n\t\t\tlog: consoleLogger,\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"remote logger\",\n\t\t\tlog: remoteLogger(\"http://log.output/api\"),\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t],\n});\n```\n\n### v1.0.0 [2019-09-12]\n\n- Initial release\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.2.0-beta.20201202.2","_nodeVersion":"14.15.0","_npmVersion":"6.14.8","dist":{"integrity":"sha512-QPyrD+JGKHUfJtLfqN5O/tSBIBzRwnawQg+ubh8pxRTQmN/ppZiIzJftpcgfh/+kZZBN7VPaNBvmqIXn+qQTLQ==","shasum":"6922f634564c1347dfd7c5adee816b862c82519d","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.2.0-beta.20201202.2.tgz","fileCount":17,"unpackedSize":87526,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfyBLLCRA9TVsSAnZWagAAtnIP/iJEX0pE4r01Sujvoqls\nTx36tNsvgP6dS2B4OEDpYeaSxcc27r2BsgEwNkkl8esPkFZCaWkfFCI5Is1V\nrfC59gVqRhjm8uSRjKKY8HA4m7yVOzmhRV9ciGTyUUCl+4fbHt5FhQSrQVoV\nyLiSJ1/K1fkZ+MxEGWANLGQdsf9yg+uM//Z1rHUEvnmJxKLL5xcK/AU2r/5b\n7zmaBaf7t98IEBKZmOndu0PC4mEa0AM9P4o95Ndg3/2zPSjHm9KOrXckwlOS\nHTdN+BExaW/J5BO/8tgFkF5XnSRMhAdN9Y7050a1I8TQb96HmbFksOG3mAhx\n1SwqApKZTHQM1VyzDQI234uObFr3YKvkOloEuaOvffrFRFAGQEcyIJrv9K3Q\n9sUuBzwO8zvSBP4R05wTz/xx0v78lL/MnLgXuU9SbsSiIs6vrgou8touoyVS\nTfpCsDcJC0s5G3jC4LE6LXUvp9UCoYtbTTqKiYeW4EeiQLlhQl0W8bJpQMO5\nsp/s6Bhcg7IIyINT/cmaq1eEl4/Tb1L90lYIEETClNEKu80xOpME0zInfjpo\nvplP9ZE1b2l/0KgpcbywCbqmkZEgLHTIFtMQpl/JsC5QE4iHhQ+LCRCt0QT1\nmE59YTnC0nJavuQjFNRFrtUCpzqBa+YfuH9MwzQrt3CU0BcIB1E9vozln6da\n28oq\r\n=CeA3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHzkR/Y6Qr3oYtXzXTrwOYvTkwChsXHHb734O9F23LLWAiALTxFLUe7O0qMJFAb1cWeeQTJWUkLN9oxp1KF0zqk4rg=="}]},"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"maintainers":[{"name":"curtis.swartzentruber","email":"c.swartzentruber@perficient.com"},{"name":"shelby-prft","email":"shelby.hagman@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"},{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"espnpm","email":"espnpm@perficient.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.2.0-beta.20201202.2_1606947531188_0.5468078485288954"},"_hasShrinkwrap":false},"2.3.0-beta.20230307.3":{"name":"@prftesp/cle-logger","version":"2.3.0-beta.20230307.3","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* -g && tsc --build && npm run build:es5","build:es5":"rimraf dist-es5/* -g && tsc --build tsconfig.es5.json","test":"jest --runInBand --silent --config jest.config.js","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^29.4.0","@types/node":"^18.14.6","@typescript-eslint/eslint-plugin":"^5.54.1","@typescript-eslint/parser":"^5.54.1","eslint":"^8.35.0","eslint-config-prettier":"^8.7.0","jest":"^29.5.0","prettier":"^2.8.4","rimraf":"^4.3.1","ts-jest":"^29.0.5","typescript":"^4.9.5"},"dependencies":{"axios":"^1.3.4","serializerr":"^1.0.3"},"readme":"﻿CLE Logger\n==\n\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\n\n## Documentation\n\n- [Target Environment](#target-environment)\n- [Simple Example](#simple-example)\n- [Installation](#installation)\n- [Features](#features)\n- [Configuration](#configuration)\n- [API](#api)\n  - [Log sinks](#log-sinks)\n  - [Logging statements](#logging-statements)\n  - [Logging parameters](#logging-parameters)\n  - [Alternative logging functions](#alternative-logging-functions)\n  - [Logging constants](#logging-constants)\n  - [Log codes](#log-codes)\n  - [Registering Twilio Actions](#registering-twilio-actions)\n  - [Twilio Event Hooks](#twilio-event-hooks)\n  - [Version](#version)\n  - [Error To Object](#error-to-object)\n- [Log Object](#log-object)\n- [Handling PII Data](#handling-pii-data)\n- [Advanced Configuration](#advanced-configuration)\n  - [Remote logging](#remote-logging)\n- [Changelog](#changelog)\n\n## Target Environment\n\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\n\n### ES5 Compatible Version\n\nIf you need an ES5 compatible version (for example you need IE11 support), use this package: https://www.npmjs.com/package/@prftesp/cle-logger-es5. **This package is not regularly released with the main library, and should be used only when absolutely necessary**. If you need to support IE11, don't forget to include the necessary polyfills, mainly for `Promise` support. To do this, you can include this script tag on the page, before you load this library `<script src=\"https://cdn.polyfill.io/v3/polyfill.min.js?features=es6\"></script>`, or visit `https://polyfill.io` for more options.\n\n**Please note** that the ES5 version is generated, and is sharing the same code and documentation as the main library. This is why the rest of the documentation doesn't mention the ES5 version in text or examples. The only difference is how you reference it (`@prftesp/cle-logger` vs `@prftesp/cle-logger-es5`). To install it use `npm install @prftesp/cle-logger-es5`, and to import it use `import { cleLogger } from \"@prftesp/cle-logger-es5\";`.\n\n## Simple Example\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"console sink\",\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\tpiiSafe: true,\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n});\n\n// will log message to console\ncleLogger.info(\"log message\");\n```\n\n## Installation\n\n```bash\n$ npm install @prftesp/cle-logger\n```\n\n## Features\n\n- Runs in Node.js and the browser\n- Remote logging\n- Multiple log sinks\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\n- Remote minimum log level overrides\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\n- Support for tagging logs with custom component names or correlation tokens\n- Send Twilio event hook data to a custom backend\n\n## Configuration\n\nTo configure the logger call the `init` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\n\n**Available options**\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// Twilio account sid, required for use with CLE\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\n\n\t// Will be associated with all logs,\n\t// if not provided, a unique uuid will be generated\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\n\n\t// An array of that will receive an array of log objects,\n\t// and can optionally return a promise. Please see the\n\t// \"Log Sinks\" section for more information.\n\tlogSinks: [\n\t\t{\n\t\t\t// name of the log sink\n\t\t\tname: \"exampleConsoleSink\",\n\t\t\t// log handler function\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\t// This flag defines if the log message is processed by this sink when the contains\n\t\t\t// PII parameter of the log statement is true. piiSafe of true indicates all log\n\t\t\t// messages are allowed (even when contains PII is true); piiSafe of false indicates\n\t\t\t// only log messages with contains PII set to false will be processed by this log sink.\n\t\t\t// Defaults to false\n\t\t\tpiiSafe: true,\n\t\t\t// log level for the sink, defaults to INFO\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n\n\t// A way to display additional errors internal to the logger, useful for debugging, defaults to false\n\tlogErrorsToConsole: false,\n\n\t// If logging Twilio Flex Actions, provide a list of actions\n\t// to be excluded from logs\n\texcludedFlexActionNames: [\"SendTyping\"],\n\n\t// A custom endpoint to fetch log level overrides\n\t// NOTE: the provided url should always end with 'accountSid=',\n\t// the library will append the accountSid\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\n\n\t// An instance of ('@twilio/flex-ui').Manager.\n\t// If provided, it will be used to get the worker sid and call sid (only during a call), and\n\t// automatically include it in 'customProperties' log property and with every log\n\ttwilioFlexManager: manager,\n\n\t// To send data from Twilio event hooks, to a custom backend,\n\t// configure one or more endpoints for each event type\n\ttwilioEventHooks: {\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\n\t},\n});\n```\n\n## API\n\nThe logger can be imported as an object in CommonJs or ES6 syntax:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n// or\nconst cleLogger = require(\"@prftesp/cle-logger\");\n```\n\nAlternatively, all top level members of the `cleLogger` are available as named exports and can be imported separately:\n\n```js\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\n\ninit({\n\tlogSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: logLevels.VERBOSE\n\t}],\n});\n\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\n\n// vs\n\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ninit({\n\tcleLogger.logSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: cleLogger.consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}],\n});\n\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\n```\n\n### Log sinks\n\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the `logSinks` config. The logSinks configuration defines an array of objects with each object defining the log sink activity. All provided log sinks will be executed in parallel. A log sink configuration must include the following properties:\n\n- `name`: this is an identifiable name for the logger and must be unique per log sink defined\n- `log`: this indicates the logic to process on each batch of logs processed by the sink. There are several built in processing functions that can be used or you can insert any code inline with this definition. The built in processing functions are `consoleLogger` which will send the log statements to the attached console or `remoteLogger` which will send the log statements to the specified url provided as a definition on the parameter (i.e. `remoteLogger(\"https://mylogger.url\"`). For a custom log processing implementation, the logic must be async operations and must return a Promise object. The custom log method will receive an array of log objects for you to handle. You can find out more about the log object type in the [Log Object](#log-object) section. Here is an example of a custom log function:\n\n```js\nlogs => {\n\t// log each received log to the console separately\n\tlogs.forEach(log => console.log(log));\n\treturn Promise.resolve();\n};\n```\n\n- `logLevel`: defines the minimum default log level that will be processed by this log sink. Log levels lower than the minimum level will not be processed by this log sink. The minimum default log level can be overridden at run time by the value returned from the overall `minLogLevelOverrideUrl` that is set up in the library `init`.\n- `piiSafe`: indicates if log messages with a `containsPII` parameter value of true should be processed by the log sink. If `containsPII` parameter value on the log message is true and piiSafe is false then the log message will not be processed by this log sink, if piiSafe is true then it will. If the `containsPII` parameter value is false on the log message than it is always processed by the log sink regardless of the piiSafe value.\n\nCustom log sink configuration example. In this example, the custom code executed for each log statement is to append the log into a file. Note how a Promise is returned as required:\n\n```js\n{\n\t// name of the log sink\n\tname: \"exampleConsoleSink\",\n\t// log handler function - this is where you put your custom handling code and\n\t// remember to always return a promise. If your operation is not asynchronous,\n\t// you can do this by returning Promise.resolve().\n\tlog: logs => {\n\t\tfs.appendFile(\n\t\t\t\"./log.txt\",\n\t\t\t`${JSON.stringify(logs)}`,\n\t\t\tconsole.log\n\t\t);\n\t\treturn Promise.resolve();\n\t},\n\t// should this log sink accept PII data, defaults to false\n\tpiiSafe: true,\n\t// log level for the sink, defaults to INFO\n\tlogLevel: cleLogger.logLevels.VERBOSE\n}\n```\n\nExample log sink configuration that uses a custom coded sink and both standard console and remote logging sinks:\n\n```js\nconst fs = require(\"fs\");\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"fileSink\",\n\t\t\tlog: logs => {\n\t\t\t\tfs.appendFile(\"./log.txt\", `${JSON.stringify(logs)}`, console.log);\n\t\t\t\treturn Promise.resolve();\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"remoteSink\",\n\t\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\n\t\t\tlogLevel: cleLogger.logLevels.WARN,\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"consoleSink\",\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n});\n```\n\n### Logging statements\n\nTo create logs, you can use one of the logging function available. They all take the same arguments, described below, and log at a corresponding log level. For example, if you use the `cleLogger.verbose` logging function, and your log sink is configured with `logLevel: cleLogger.logLevels.VERBOSE`, that log sink will receive the log. If your log sink is configured with a higher log level, for example `logLevel: cleLogger.logLevels.INFO`, then it would not receive the verbose log.\n\nHere are the available logging functions:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(...);\ncleLogger.info(...);\ncleLogger.warn(...);\ncleLogger.error(...);\ncleLogger.critical(...);\n```\n\nThe functions are all async, return `Promise<void>`, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\n\n### Logging parameters\n\nAll logging functions take the same parameters:\n\n- `message`: the only required property, no logs will be generated without it\n  - type: string\n  - required: yes\n- `additionalData`: any additional log data you would like to include with the log, can be any data type\n  - type: any\n  - required: no\n  - default value: null\n- `customProperties`: a single level deep object of important properties you would like to distinguish from general log data, like various sids CLE uses it to index these properties in Application Insights. For CLE, try to use keys found in `cleLogger.customPropertyKeys`\n  - type: object\n  - required: no\n  - default value: {}\n- `componentName`: another useful way to 'tag' your logs for easier searching. Can be any string, but for CLE, try to use available options in `cleLogger.componentNames`\n  - type: string\n  - required: no\n  - default value: \"\"\n- `logCode`: log code, can be any number used to categorize logs. For defined ranges see the [log codes](#log-codes) section.\n  - type: number\n  - required: no\n  - default value: 0\n- `correlationToken`: correlation token to be associated with this log only\n  - type: string\n  - required: no\n  - default value: token provided in `init`, or a generated token if one wasn't provided\n- `containsPII`: does the log contain PII data, defaults to false. If set to true, the log will be passed in only to those log sinks that have `piiSafe` set to true. For more info see the [handling PII data](#handling-pii-data) section.\n  - type: boolean\n  - required: no\n  - default value: false\n\n### Alternative logging functions\n\nIf you would prefer to pass in an object to logging functions, instead of using a long list of parameters, you can use the new logging functions available under the `cleLogger.v2` namespace. These functions are namespaced to keep backwards compatibility, but you should feel free to use them going forward. The behavior of these functions is exactly the same as with those listed in [logging statements](#logging-statements) section.\n\nExample verbose logging statement with both versions of the logging function:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(\n\t\"Log message\", // message\n\t{ data: \"test\" }, // additionalData\n\t{ [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" }, // customProperties\n\tcleLogger.componentNames.SESSION, // componentName\n\t70123, // logCode\n\t\"my-token\", // correlationToken\n\ttrue, // containsPII\n);\n\ncleLogger.v2.verbose(\n\t\"Log message\",\n\t{\n\t\tadditionalData: { data: \"test\" },\n\t\tcustomProperties: { [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" },\n\t\tcomponentName: cleLogger.componentNames.SESSION,\n\t\tlogCode: 70123,\n\t\tcorrelationToken: \"my-token\",\n\t\tcontainsPII: true,\n\t}\n);\n```\n\nAs you can see in the examples above, both functions take in the same values, but the `v2` version makes it a bit easier to see which value corresponds to each key. It comes down to personal preference, use whichever you prefer.\n\n### Logging constants\n\n**Logging levels**\n\nLogging level constants should be used when configuring the log level of a [log sink](#log-sinks).\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.logLevels.VERBOSE; // verbose\ncleLogger.logLevels.INFO; // info\ncleLogger.logLevels.WARN; // warn\ncleLogger.logLevels.ERROR; // error\ncleLogger.logLevels.CRITICAL; // critical\n```\n\n**Component names**\n\nDefines a set of commonly used components that should be used for the `componentName` parameter value of a log statement if you are logging from one of these common components in a Twilio implementation.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.componentNames.SESSION;\ncleLogger.componentNames.DIALER;\ncleLogger.componentNames.DASHBOARD;\ncleLogger.componentNames.AFTERCALL;\ncleLogger.componentNames.SUPERVISOR;\n```\n\n**Custom property keys**\n\nDefines a set of commonly used key that should be used when defining the custom properties object that is supplied in the `customProperties` parameter of any logging statement.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.customPropertyKeys.CHANNEL_SID;\ncleLogger.customPropertyKeys.TASK_SID;\ncleLogger.customPropertyKeys.CALL_SID;\ncleLogger.customPropertyKeys.WORKFLOW_SID;\ncleLogger.customPropertyKeys.WORKER_SID;\n```\n\n### Log codes\n\nIt can be helpful when investigating issues to only view logs related to specific areas of a system. Use Log Codes to give a category definition for what area of the contact center the log activity occurred within. Use numbers within the following pre-defined log code ranges or define custom log codes by using your own numbers higher than 300,000.\n\n- 100 000 - 109 999 => CALL_STATUS\n- 110 000 - 119 999 => IVR\n- 120 000 - 129 999 => ROUTING\n- 130 000 - 139 999 => AGENT_ACTION\n- 140 000 - 149 999 => TRANSFER\n- 150 000 - 159 999 => RM_INTEGRATION\n- 160 000 - 169 999 => WFM_INTEGRATION\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\n\n### Registering Twilio Actions\n\nIt is important to log information from the Twilio Flex UI for all implementations even when there is little customization happening. Many developers find it useful to generate log activity when some, or any, of the Twilio Actions occur within the Twilio Flex UI. Twilio Actions are events that arise from the Twilio Flex UI base implementation and indicate when certain activity is occurring - such as an agent hanging up a call. The CLE Logger library can be setup to automatically register all, or selected, Twilio Actions such that right after any code tied to the Action event has been executed a log statement is written indicating the Action occurred. This log statement will also automatically include the action event payload. To enable this logging capability, use the following approach to register the Twilio Actions. This only needs to be performed once during the initialization of the Twilio Flex UI page on the client.\n\nExample in a Twilio Flex UI plugin:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n/**\n * @param flex { typeof import('@twilio/flex-ui') }\n * @param manager { import('@twilio/flex-ui').Manager }\n */\ninit(flex, manager) {\n\tcleLogger.init({\n\t\taccountSid: \"your-account-sid\"\n\t});\n\n\tcleLogger.registerTwilioActions(flex);\n}\n```\n\n**Note**: A Twilio account sid must be provided during `init` with the `accountSid` parameter, otherwise the actions won't be logged.\n\nThe logger will loop through all actions found under `flex.Actions.actions`, and attach `after` event handlers to log the action name and payload. Any private properties found in the payload (starting with '\\_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Not all Action events need to be logged and by using the `excludedFlexActionNames` property in the logger configuration the Action events not desired can be ignored. Any actions names configured in `excludedFlexActionNames` will not be included in the logs.\n\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\n\n**Twilio Actions log levels**\n\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of `INFO`.\n\n```js\nAcceptTask: INFO;\nCancelTransfer: INFO;\nCompleteTask: INFO;\nHangupCall: INFO;\nHideDirectory: VERBOSE;\nHistoryGo: VERBOSE;\nHistoryGoBack: VERBOSE;\nHistoryGoForward: VERBOSE;\nHistoryPush: VERBOSE;\nHistoryReplace: VERBOSE;\nHoldCall: INFO;\nHoldParticipant: INFO;\nKickParticipant: INFO;\nLogout: INFO;\nMonitorCall: INFO;\nNavigateToView: INFO;\nRejectTask: INFO;\nSelectTask: INFO;\nSelectTaskInSupervisor: INFO;\nSelectWorkerInSupervisor: INFO;\nSendMessage: INFO;\nSendTyping: VERBOSE;\nSetActivity: INFO;\nSetInputText: VERBOSE;\nShowDirectory: VERBOSE;\nStopMonitoringCall: INFO;\nToggleMute: INFO;\nToggleSidebar: VERBOSE;\nTransferTask: INFO;\nUnholdCall: INFO;\nUnholdParticipant: INFO;\nWrapupTask: INFO;\n```\n\n### Twilio Event Hooks\n\nCurrently, the Twilio console only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with `twilioEventHooks` option, and use the following functions to send event data to a custom endpoint.\n\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is `application/x-www-form-urlencoded` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// unrelated init properties omitted from this example.\n\t// include in the twilioEventHooks array an entry for only the event hooks\n\t// the executing code is going to relay. For example, if the code is only\n\t// handling task router web hook events then you would only need to define\n\t// the taskRouter relay url here.\n\ttwilioEventHooks: {\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\n\t},\n});\n\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\n```\n\nExample Azure Function hook for debugger events:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\nmodule.exports = async function(context, req) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\t},\n\t});\n\n\t// log to console\n\tcleLogger.info(\"Received Twilio debugger event\");\n\n\t// send event data to a configured url\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\n};\n```\n\n**NOTE WHEN USING THIS FUNCTIONALITY IN TWILIO FUNCTIONS:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to `x-www-form-urlencoded` format.\n\nExample Twilio function task router relay using the `form-urlencoded` npm package to convert the data to `x-www-form-urlencoded` format:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\nconst formurlencoded = require(\"form-urlencoded\").default;\n\nexports.handler = async function(context, event, callback) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\t},\n\t});\n\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\n\n\tcallback();\n};\n```\n\n### Version\n\nVersion will be automatically included in all logs.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.version; // 1.0.0\n```\n\n### Error To Object\n\nUtility function to convert an `Error` object to a plain JS object, with all properties preserved. When serialized, `Error` objects will omit custom properties from the output which makes debugging difficult. This utility will traverse all properties in the `Error` object, and output a plain JS object with all properties including the stack trace. See an example below.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\nconst err = new Error(\"error message\");\nconsole.log(err); // \"Error: error message\\n    at Object.<anonymous> …\"\n\nconst errObject = cleLogger.errorToObject(err);\nconsole.log(errObjct); // {name: 'Error', message: 'error message', stack: 'Error: error message\\n    at Object.<anonymous> …'}\n```\n\nExample usage when logging errors:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ntry {\n\t// some code that can throw an error\n} catch (error) {\n\tcleLogger.error(\"My error message\", cleLogger.errorToObject(error));\n}\n```\n\n## Log Object\n\nThe following is the type definition of the log object received by [log sinks](#log-sinks). It consists of properties provided in log messages, as well as some that are automatically inserted like the `eventDate`. You can use this information to customize your logs inside a custom log sink (e.g. filter out unwanted logs, modify some of the properties to include more data, or any kind of conditional logic based on the value of these properties). If using the built in `remoteLogger` log processor with your custom backend, you can expect an array of these objects to be sent to your log endpoint. For more info about implementing a custom logging endpoint see the [remote logging](#remote-logging) section.\n\n```js\n{\n\t// Account sid if provided during init\n\taccountSid: string;\n\n\t// Any additional log data to be included with the log\n\tadditionalData: unknown;\n\n\t// Component name, if provided\n\tcomponentName: string;\n\n\t// Generated or configured token\n\tcorrelationToken: string;\n\n\t// Current UTC datetime in ISO 8601 format\n\teventDate: string;\n\n\t// Log level\n\tlevel: string;\n\n\t// Log code, if provided\n\tlogCode: number;\n\n\t// Log message\n\tmessage: string;\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\tproperties: object;\n\n\t// BrowserFunction if the environment is the browser\n\t// CloudFunction if the environment is Node.js\n\tsource: string;\n\n\t// Indicates that the log contains PII data\n\tcontainsPII: boolean;\n\n\t// Logging library version\n\tloggerVersion: string;\n}\n```\n\n## Handling PII Data\n\nThe logger provides support for helping to protect log messages or data that need to include PII (Personally Identifiable Information). While the logger has several mechanisms that can be used to flag logs that contain PII and block PII from going to particular log sinks, it is still ultimately the developers responsibility to ensure PII is flagged or marked appropriately for the logger to know when it exists - the logger cannot automatically detect if PII is part of a log statement or data object. The general guideline is that developers should avoid including any PII data in log information whenever possible. In the instances where PII data must be included there are several mechanisms the logger supports to help indicate PII data and protect where it is sent.\n\nA `containsPII` parameter exists on log statement methods. This flag should be set to true if there is PII data, or any potential chance of it, in the log message, additional data or custom properties data. When this flag is set to true then it will only allow log sinks that have the `piiSafe` configuration set to true to receive and process the log message. `piiSafe` indicates on a log sink whether that log sink receiver is able to internally store PII data OR has logic to strip PII data that is tagged in the message or from pre-configured additional data fields before the log is written to any storage or output device. When sending data to any `remoteLogger` endpoint the log sink can be marked `piiSafe` only as long as data is appropriately tagged in messages and/or the names of custom properties that might have PII values in additional data are pre-configured AND the endpoint implementer has verified and ensured their receiving code will remove marked PII data immediately before sending logs to any output or storage media.\n\nTo tag any part of the message parameter in a log statement as containing PII within the actual log message use the `tagPII` utility. The `tagPII` utility will indicate to a remote backend that the text between the tags should be stripped or masked before sending logs to any output or storage media.\n\nExample tagged output:\n\n```js\nimport { tagPII } from \"@prftesp/cle-logger\";\n\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\n```\n\n## Advanced Configuration\n\n### Remote logging\n\nThe library provides a built in way to transmit logs to a remote http endpoint using the `remoteLogger` logs processor. The `remoteLogger` can be directed at a known listener such as CLE or you can create your own custom log listener. In addition, the log level in effect can be controlled remotely setting the `minLogLevelOverrideUrl` to an http address that will return the current log level to use instead of any defined default logLevel on each `logSink`. The `minLogLevelOverrideUrl` can point to a known central API such as CLE or you can create your own endpoint.\n\nThe following sections outline how to create your own endpoints for either receiving log messages from the `remoteLogger` log sink or providing the current log level override to it.\n\n**Remote log endpoint**\n\nTo implement your own remote logging endpoint, all you have to do is accept `POST` requests, with a JSON payload of an array of [log objects](#log-object), and return either `{ success: true }` or `{ success: false }`. The endpoint should always return a `200 OK` status even if your server code fails so that the CLE logger does not cause failures in the business logic in which it is embedded. A good use-case would be leveraging a serverless api platform to output logs to a queue.\n\nExample of an Azure Function that can receive requests from the `remoteLogger` log sink:\n\n```js\nmodule.exports = async function(context, req) {\n\tcontext.log(req.body); // body will have the log array\n\n\tcontext.res = {\n\t\tstatus: 200,\n\t\tbody: { success: true },\n\t};\n};\n```\n\n**Remote log level override**\n\nThe `minLogLevelOverrideUrl` configuration property is used to provide an endpoint that accepts `GET` requests and a `accountSid` query string parameter. `accountSid` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the `minLogLevel` configuration property. If null is returned, the `minLogLevel` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a `200 OK` status code and use the `success` property to indicate failure.\n\nExample success response:\n\n```json\n{\n\t\"success\": true,\n\t\"minLogLevel\": \"warn\"\n}\n```\n\nExample error response:\n\n```json\n{\n\t\"success\": false,\n\t\"minLogLevel\": null\n}\n```\n\n## Changelog\n\n### v2.3.0\n\n**New features**\n\n- To address long-running log requests caused by occasional slow remote log responses, this version introduces default timeouts for log requests. Timeout for Twilio event hooks is 2000 ms and is not configurable. Timeout for the `remoteLogger` helper is 500 ms and can be adjusted with an optional second parameter: `remoteLogger(\"https://mylogger.url\", 1000);`\n\n### v2.2.0\n\n**New features**\n\n- Added [alternative logging functions](#alternative-logging-functions) that have a much shorter parameter list, and allow you to specify only those logging properties that you want, without the need to pass in `null` for parameters you are not interested in. New functions are available under `cleLogger.v2` for backward compatibility reasons.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n// if you want to log just the message and the token, \n// you need to default parameters to null until you get to token\ncleLogger.verbose(\"Log message\", null, null, null, null, \"my-token\");\n\n// with the new functions, you just provide an object with the properties you want\ncleLogger.v2.verbose(\"Log message\", { correlationToken: \"my-token\" });\n```\n\n### v2.1.0\n\n**New features**\n\n- Added [Error To Object](#error-to-object) method to assist users in converting error object to plain objects.\n\n### v2.0.7\n\n**New features**\n\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the call sid during a call, and automatically include it in `customProperties` log property for all logs that happen during that call (including Flex Action logs).\n\n### v2.0.5\n\n**New features**\n\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the worker sid and automatically include it in `customProperties` log property.\n\n**Other updates**\n\n- Updated documentation based on user feedback.\n\n### v2.0.4\n\n**Bug fixes**\n\n- Fixed issue with logging to console in Twilio Functions. The `consoleLogger` will now output the log object as serialized json.\n\n### v2.0.0 [2019-12-20]\n\n**New features**\n\n- All logging output is now configured using the `logSinks` property. The library provides two commonly used loggers - `consoleLogger` and a `remoteLogger` that accepts the log url as a parameter. [Configuration](#configuration)\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\n- Log functions have a new optional `containsPII` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\n- A new helper function `tagPII` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\n\n**Breaking changes**\n\n- `customLogSink` configuration renamed to `logSinks`\n- Removed `disableRemoteLogging` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\n- Removed `logToConsole` configuration. This is now configured as a log sink with a `consoleLogger` helper function.\n- Removed `logUrl` configuration. This is now configured as a log sink with a `remoteLogger` helper function.\n- Removed `minLogLevel` configuration. This is now configured per log sink.\n\n**Migration guide**\n\n```js\nimport cleLogger from \"@prftesp/cle-logger\";\n\n// v1.0.0 and older config\ncleLogger.init({\n\tdisableRemoteLogSinks: true, // no direct replacement\n\tlogToConsole: true, // replaced by the \"console logger\" sink\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\n\tcustomLogSinks: [\n\t\t{\n\t\t\tname: \"custom sink\",\n\t\t\tlog: logs => {\n\t\t\t\t/* your custom logging */\n\t\t\t},\n\t\t},\n\t],\n});\n\n// v2.0.0 config\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"custom sink\",\n\t\t\tlog: logs => {\n\t\t\t\t/* your custom logging */\n\t\t\t},\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"console logger\",\n\t\t\tlog: consoleLogger,\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"remote logger\",\n\t\t\tlog: remoteLogger(\"http://log.output/api\"),\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t],\n});\n```\n\n### v1.0.0 [2019-09-12]\n\n- Initial release\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.3.0-beta.20230307.3","_nodeVersion":"18.14.2","_npmVersion":"9.5.0","dist":{"integrity":"sha512-ih/vN/qxPO1FCvAUWu4OGzy776++JKrd49xKJj1pVRm0osJq8os+hn8HiuWimnAgvPB2c0gHWtFRKs6S4thBvw==","shasum":"e000ccbb2b4eb83d7a243dce9b8819af07645245","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.3.0-beta.20230307.3.tgz","fileCount":17,"unpackedSize":90635,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDP5qpoAtlazh0GsKkp3dSvRWv9C1DLzQUBOkAdfrntDwIhAP+1tP4ZQ98mMuv9yFCXpTVK8GXqdf8CYSLHITAYCvsM"}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkB6htACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqpBhAAjpWlAn+GtXAzt0GepVFA/YRYsgvDZnsD45cswVUkp0SNIjFN\r\n0MVgniy0vQ0HBV2LGyG8F24sbwJsEvilfbZdoEyEnK/KDFFml9llFOMtVglq\r\npxQCRv8mLf3iVp0MRWlsrWsOqIHpD1kJbRKLpEB1N33xqtCey0aUUzzszGnD\r\n1s34dbecwpqlMEDDnDkdSNBGIZOwkC+aavxu/DT9tSHXtDbrFYkjd6l+XN3z\r\ngr/y+uGT1JwKFZhioKdWlMw4zKj29NN5vlFMWnVeEt0gwvyZBo3H+xrF2hRj\r\nNJVnbhLaD0DZmf+wOUunYpXfh8RYWN0wgIscF5M3UpecFn1F/CpexuvYbBaG\r\nFubKquuzIrphMhxtW/2UbKDr3l7a2eb3AFMeKHFh1dCxXM/aeKpMODAMY5Br\r\nPkSzv5Kpz/9o2BjIUWDU2eVjKhxUP3lddV68QSrKZmXcmFem7bLcdstCmhvG\r\ncW6eGWuBhhJm9iwdAo5Sc6UudXC7bhOlZylDuHziSgjijW+zOb1oKIDYW8DO\r\nRszbm+VCDgN3z1kskm9Ts7uIpkPvKgJWavf/iSEn/g109ZsE9zmd6gf1RQmS\r\nTPrjfHlFobU6L4SDQ8Qtj23S6jXNA8maeKwag2o/TtTLDQYG5MYdQB+jQ/cH\r\nKKaHhkU0Up51b6GbCQ2FeJQdGIVB2Z/CyrY=\r\n=VCDs\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"maintainers":[{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"},{"name":"shelby-prft","email":"shelby.hagman@perficient.com"},{"name":"curtis.swartzentruber","email":"c.swartzentruber@perficient.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.3.0-beta.20230307.3_1678223469011_0.7774592117142061"},"_hasShrinkwrap":false},"2.3.0-beta.20230307.4":{"name":"@prftesp/cle-logger","version":"2.3.0-beta.20230307.4","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* -g && tsc --build && npm run build:es5","build:es5":"rimraf dist-es5/* -g && tsc --build tsconfig.es5.json","test":"jest --runInBand --silent --config jest.config.js","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^29.4.0","@types/node":"^18.14.6","@typescript-eslint/eslint-plugin":"^5.54.1","@typescript-eslint/parser":"^5.54.1","eslint":"^8.35.0","eslint-config-prettier":"^8.7.0","jest":"^29.5.0","prettier":"^2.8.4","rimraf":"^4.3.1","ts-jest":"^29.0.5","typescript":"^4.9.5"},"dependencies":{"axios":"^1.3.4","serializerr":"^1.0.3"},"readme":"﻿CLE Logger\n==\n\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\n\n## Documentation\n\n- [Target Environment](#target-environment)\n- [Simple Example](#simple-example)\n- [Installation](#installation)\n- [Features](#features)\n- [Configuration](#configuration)\n- [API](#api)\n  - [Log sinks](#log-sinks)\n  - [Logging statements](#logging-statements)\n  - [Logging parameters](#logging-parameters)\n  - [Alternative logging functions](#alternative-logging-functions)\n  - [Logging constants](#logging-constants)\n  - [Log codes](#log-codes)\n  - [Registering Twilio Actions](#registering-twilio-actions)\n  - [Twilio Event Hooks](#twilio-event-hooks)\n  - [Version](#version)\n  - [Error To Object](#error-to-object)\n- [Log Object](#log-object)\n- [Handling PII Data](#handling-pii-data)\n- [Advanced Configuration](#advanced-configuration)\n  - [Remote logging](#remote-logging)\n- [Changelog](#changelog)\n\n## Target Environment\n\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\n\n### ES5 Compatible Version\n\nIf you need an ES5 compatible version (for example you need IE11 support), use this package: https://www.npmjs.com/package/@prftesp/cle-logger-es5. **This package is not regularly released with the main library, and should be used only when absolutely necessary**. If you need to support IE11, don't forget to include the necessary polyfills, mainly for `Promise` support. To do this, you can include this script tag on the page, before you load this library `<script src=\"https://cdn.polyfill.io/v3/polyfill.min.js?features=es6\"></script>`, or visit `https://polyfill.io` for more options.\n\n**Please note** that the ES5 version is generated, and is sharing the same code and documentation as the main library. This is why the rest of the documentation doesn't mention the ES5 version in text or examples. The only difference is how you reference it (`@prftesp/cle-logger` vs `@prftesp/cle-logger-es5`). To install it use `npm install @prftesp/cle-logger-es5`, and to import it use `import { cleLogger } from \"@prftesp/cle-logger-es5\";`.\n\n## Simple Example\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"console sink\",\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\tpiiSafe: true,\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n});\n\n// will log message to console\ncleLogger.info(\"log message\");\n```\n\n## Installation\n\n```bash\n$ npm install @prftesp/cle-logger\n```\n\n## Features\n\n- Runs in Node.js and the browser\n- Remote logging\n- Multiple log sinks\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\n- Remote minimum log level overrides\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\n- Support for tagging logs with custom component names or correlation tokens\n- Send Twilio event hook data to a custom backend\n\n## Configuration\n\nTo configure the logger call the `init` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\n\n**Available options**\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// Twilio account sid, required for use with CLE\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\n\n\t// Will be associated with all logs,\n\t// if not provided, a unique uuid will be generated\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\n\n\t// An array of that will receive an array of log objects,\n\t// and can optionally return a promise. Please see the\n\t// \"Log Sinks\" section for more information.\n\tlogSinks: [\n\t\t{\n\t\t\t// name of the log sink\n\t\t\tname: \"exampleConsoleSink\",\n\t\t\t// log handler function\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\t// This flag defines if the log message is processed by this sink when the contains\n\t\t\t// PII parameter of the log statement is true. piiSafe of true indicates all log\n\t\t\t// messages are allowed (even when contains PII is true); piiSafe of false indicates\n\t\t\t// only log messages with contains PII set to false will be processed by this log sink.\n\t\t\t// Defaults to false\n\t\t\tpiiSafe: true,\n\t\t\t// log level for the sink, defaults to INFO\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n\n\t// A way to display additional errors internal to the logger, useful for debugging, defaults to false\n\tlogErrorsToConsole: false,\n\n\t// If logging Twilio Flex Actions, provide a list of actions\n\t// to be excluded from logs\n\texcludedFlexActionNames: [\"SendTyping\"],\n\n\t// A custom endpoint to fetch log level overrides\n\t// NOTE: the provided url should always end with 'accountSid=',\n\t// the library will append the accountSid\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\n\n\t// An instance of ('@twilio/flex-ui').Manager.\n\t// If provided, it will be used to get the worker sid and call sid (only during a call), and\n\t// automatically include it in 'customProperties' log property and with every log\n\ttwilioFlexManager: manager,\n\n\t// To send data from Twilio event hooks, to a custom backend,\n\t// configure one or more endpoints for each event type\n\ttwilioEventHooks: {\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\n\t},\n});\n```\n\n## API\n\nThe logger can be imported as an object in CommonJs or ES6 syntax:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n// or\nconst cleLogger = require(\"@prftesp/cle-logger\");\n```\n\nAlternatively, all top level members of the `cleLogger` are available as named exports and can be imported separately:\n\n```js\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\n\ninit({\n\tlogSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: logLevels.VERBOSE\n\t}],\n});\n\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\n\n// vs\n\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ninit({\n\tcleLogger.logSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: cleLogger.consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}],\n});\n\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\n```\n\n### Log sinks\n\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the `logSinks` config. The logSinks configuration defines an array of objects with each object defining the log sink activity. All provided log sinks will be executed in parallel. A log sink configuration must include the following properties:\n\n- `name`: this is an identifiable name for the logger and must be unique per log sink defined\n- `log`: this indicates the logic to process on each batch of logs processed by the sink. There are several built in processing functions that can be used or you can insert any code inline with this definition. The built in processing functions are `consoleLogger` which will send the log statements to the attached console or `remoteLogger` which will send the log statements to the specified url provided as a definition on the parameter (i.e. `remoteLogger(\"https://mylogger.url\"`). For a custom log processing implementation, the logic must be async operations and must return a Promise object. The custom log method will receive an array of log objects for you to handle. You can find out more about the log object type in the [Log Object](#log-object) section. Here is an example of a custom log function:\n\n```js\nlogs => {\n\t// log each received log to the console separately\n\tlogs.forEach(log => console.log(log));\n\treturn Promise.resolve();\n};\n```\n\n- `logLevel`: defines the minimum default log level that will be processed by this log sink. Log levels lower than the minimum level will not be processed by this log sink. The minimum default log level can be overridden at run time by the value returned from the overall `minLogLevelOverrideUrl` that is set up in the library `init`.\n- `piiSafe`: indicates if log messages with a `containsPII` parameter value of true should be processed by the log sink. If `containsPII` parameter value on the log message is true and piiSafe is false then the log message will not be processed by this log sink, if piiSafe is true then it will. If the `containsPII` parameter value is false on the log message than it is always processed by the log sink regardless of the piiSafe value.\n\nCustom log sink configuration example. In this example, the custom code executed for each log statement is to append the log into a file. Note how a Promise is returned as required:\n\n```js\n{\n\t// name of the log sink\n\tname: \"exampleConsoleSink\",\n\t// log handler function - this is where you put your custom handling code and\n\t// remember to always return a promise. If your operation is not asynchronous,\n\t// you can do this by returning Promise.resolve().\n\tlog: logs => {\n\t\tfs.appendFile(\n\t\t\t\"./log.txt\",\n\t\t\t`${JSON.stringify(logs)}`,\n\t\t\tconsole.log\n\t\t);\n\t\treturn Promise.resolve();\n\t},\n\t// should this log sink accept PII data, defaults to false\n\tpiiSafe: true,\n\t// log level for the sink, defaults to INFO\n\tlogLevel: cleLogger.logLevels.VERBOSE\n}\n```\n\nExample log sink configuration that uses a custom coded sink and both standard console and remote logging sinks:\n\n```js\nconst fs = require(\"fs\");\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"fileSink\",\n\t\t\tlog: logs => {\n\t\t\t\tfs.appendFile(\"./log.txt\", `${JSON.stringify(logs)}`, console.log);\n\t\t\t\treturn Promise.resolve();\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"remoteSink\",\n\t\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\n\t\t\tlogLevel: cleLogger.logLevels.WARN,\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"consoleSink\",\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n});\n```\n\n### Logging statements\n\nTo create logs, you can use one of the logging function available. They all take the same arguments, described below, and log at a corresponding log level. For example, if you use the `cleLogger.verbose` logging function, and your log sink is configured with `logLevel: cleLogger.logLevels.VERBOSE`, that log sink will receive the log. If your log sink is configured with a higher log level, for example `logLevel: cleLogger.logLevels.INFO`, then it would not receive the verbose log.\n\nHere are the available logging functions:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(...);\ncleLogger.info(...);\ncleLogger.warn(...);\ncleLogger.error(...);\ncleLogger.critical(...);\n```\n\nThe functions are all async, return `Promise<void>`, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\n\n### Logging parameters\n\nAll logging functions take the same parameters:\n\n- `message`: the only required property, no logs will be generated without it\n  - type: string\n  - required: yes\n- `additionalData`: any additional log data you would like to include with the log, can be any data type\n  - type: any\n  - required: no\n  - default value: null\n- `customProperties`: a single level deep object of important properties you would like to distinguish from general log data, like various sids CLE uses it to index these properties in Application Insights. For CLE, try to use keys found in `cleLogger.customPropertyKeys`\n  - type: object\n  - required: no\n  - default value: {}\n- `componentName`: another useful way to 'tag' your logs for easier searching. Can be any string, but for CLE, try to use available options in `cleLogger.componentNames`\n  - type: string\n  - required: no\n  - default value: \"\"\n- `logCode`: log code, can be any number used to categorize logs. For defined ranges see the [log codes](#log-codes) section.\n  - type: number\n  - required: no\n  - default value: 0\n- `correlationToken`: correlation token to be associated with this log only\n  - type: string\n  - required: no\n  - default value: token provided in `init`, or a generated token if one wasn't provided\n- `containsPII`: does the log contain PII data, defaults to false. If set to true, the log will be passed in only to those log sinks that have `piiSafe` set to true. For more info see the [handling PII data](#handling-pii-data) section.\n  - type: boolean\n  - required: no\n  - default value: false\n\n### Alternative logging functions\n\nIf you would prefer to pass in an object to logging functions, instead of using a long list of parameters, you can use the new logging functions available under the `cleLogger.v2` namespace. These functions are namespaced to keep backwards compatibility, but you should feel free to use them going forward. The behavior of these functions is exactly the same as with those listed in [logging statements](#logging-statements) section.\n\nExample verbose logging statement with both versions of the logging function:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(\n\t\"Log message\", // message\n\t{ data: \"test\" }, // additionalData\n\t{ [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" }, // customProperties\n\tcleLogger.componentNames.SESSION, // componentName\n\t70123, // logCode\n\t\"my-token\", // correlationToken\n\ttrue, // containsPII\n);\n\ncleLogger.v2.verbose(\n\t\"Log message\",\n\t{\n\t\tadditionalData: { data: \"test\" },\n\t\tcustomProperties: { [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" },\n\t\tcomponentName: cleLogger.componentNames.SESSION,\n\t\tlogCode: 70123,\n\t\tcorrelationToken: \"my-token\",\n\t\tcontainsPII: true,\n\t}\n);\n```\n\nAs you can see in the examples above, both functions take in the same values, but the `v2` version makes it a bit easier to see which value corresponds to each key. It comes down to personal preference, use whichever you prefer.\n\n### Logging constants\n\n**Logging levels**\n\nLogging level constants should be used when configuring the log level of a [log sink](#log-sinks).\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.logLevels.VERBOSE; // verbose\ncleLogger.logLevels.INFO; // info\ncleLogger.logLevels.WARN; // warn\ncleLogger.logLevels.ERROR; // error\ncleLogger.logLevels.CRITICAL; // critical\n```\n\n**Component names**\n\nDefines a set of commonly used components that should be used for the `componentName` parameter value of a log statement if you are logging from one of these common components in a Twilio implementation.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.componentNames.SESSION;\ncleLogger.componentNames.DIALER;\ncleLogger.componentNames.DASHBOARD;\ncleLogger.componentNames.AFTERCALL;\ncleLogger.componentNames.SUPERVISOR;\n```\n\n**Custom property keys**\n\nDefines a set of commonly used key that should be used when defining the custom properties object that is supplied in the `customProperties` parameter of any logging statement.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.customPropertyKeys.CHANNEL_SID;\ncleLogger.customPropertyKeys.TASK_SID;\ncleLogger.customPropertyKeys.CALL_SID;\ncleLogger.customPropertyKeys.WORKFLOW_SID;\ncleLogger.customPropertyKeys.WORKER_SID;\n```\n\n### Log codes\n\nIt can be helpful when investigating issues to only view logs related to specific areas of a system. Use Log Codes to give a category definition for what area of the contact center the log activity occurred within. Use numbers within the following pre-defined log code ranges or define custom log codes by using your own numbers higher than 300,000.\n\n- 100 000 - 109 999 => CALL_STATUS\n- 110 000 - 119 999 => IVR\n- 120 000 - 129 999 => ROUTING\n- 130 000 - 139 999 => AGENT_ACTION\n- 140 000 - 149 999 => TRANSFER\n- 150 000 - 159 999 => RM_INTEGRATION\n- 160 000 - 169 999 => WFM_INTEGRATION\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\n\n### Registering Twilio Actions\n\nIt is important to log information from the Twilio Flex UI for all implementations even when there is little customization happening. Many developers find it useful to generate log activity when some, or any, of the Twilio Actions occur within the Twilio Flex UI. Twilio Actions are events that arise from the Twilio Flex UI base implementation and indicate when certain activity is occurring - such as an agent hanging up a call. The CLE Logger library can be setup to automatically register all, or selected, Twilio Actions such that right after any code tied to the Action event has been executed a log statement is written indicating the Action occurred. This log statement will also automatically include the action event payload. To enable this logging capability, use the following approach to register the Twilio Actions. This only needs to be performed once during the initialization of the Twilio Flex UI page on the client.\n\nExample in a Twilio Flex UI plugin:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n/**\n * @param flex { typeof import('@twilio/flex-ui') }\n * @param manager { import('@twilio/flex-ui').Manager }\n */\ninit(flex, manager) {\n\tcleLogger.init({\n\t\taccountSid: \"your-account-sid\"\n\t});\n\n\tcleLogger.registerTwilioActions(flex);\n}\n```\n\n**Note**: A Twilio account sid must be provided during `init` with the `accountSid` parameter, otherwise the actions won't be logged.\n\nThe logger will loop through all actions found under `flex.Actions.actions`, and attach `after` event handlers to log the action name and payload. Any private properties found in the payload (starting with '\\_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Not all Action events need to be logged and by using the `excludedFlexActionNames` property in the logger configuration the Action events not desired can be ignored. Any actions names configured in `excludedFlexActionNames` will not be included in the logs.\n\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\n\n**Twilio Actions log levels**\n\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of `INFO`.\n\n```js\nAcceptTask: INFO;\nCancelTransfer: INFO;\nCompleteTask: INFO;\nHangupCall: INFO;\nHideDirectory: VERBOSE;\nHistoryGo: VERBOSE;\nHistoryGoBack: VERBOSE;\nHistoryGoForward: VERBOSE;\nHistoryPush: VERBOSE;\nHistoryReplace: VERBOSE;\nHoldCall: INFO;\nHoldParticipant: INFO;\nKickParticipant: INFO;\nLogout: INFO;\nMonitorCall: INFO;\nNavigateToView: INFO;\nRejectTask: INFO;\nSelectTask: INFO;\nSelectTaskInSupervisor: INFO;\nSelectWorkerInSupervisor: INFO;\nSendMessage: INFO;\nSendTyping: VERBOSE;\nSetActivity: INFO;\nSetInputText: VERBOSE;\nShowDirectory: VERBOSE;\nStopMonitoringCall: INFO;\nToggleMute: INFO;\nToggleSidebar: VERBOSE;\nTransferTask: INFO;\nUnholdCall: INFO;\nUnholdParticipant: INFO;\nWrapupTask: INFO;\n```\n\n### Twilio Event Hooks\n\nCurrently, the Twilio console only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with `twilioEventHooks` option, and use the following functions to send event data to a custom endpoint.\n\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is `application/x-www-form-urlencoded` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// unrelated init properties omitted from this example.\n\t// include in the twilioEventHooks array an entry for only the event hooks\n\t// the executing code is going to relay. For example, if the code is only\n\t// handling task router web hook events then you would only need to define\n\t// the taskRouter relay url here.\n\ttwilioEventHooks: {\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\n\t},\n});\n\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\n```\n\nExample Azure Function hook for debugger events:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\nmodule.exports = async function(context, req) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\t},\n\t});\n\n\t// log to console\n\tcleLogger.info(\"Received Twilio debugger event\");\n\n\t// send event data to a configured url\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\n};\n```\n\n**NOTE WHEN USING THIS FUNCTIONALITY IN TWILIO FUNCTIONS:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to `x-www-form-urlencoded` format.\n\nExample Twilio function task router relay using the `form-urlencoded` npm package to convert the data to `x-www-form-urlencoded` format:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\nconst formurlencoded = require(\"form-urlencoded\").default;\n\nexports.handler = async function(context, event, callback) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\t},\n\t});\n\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\n\n\tcallback();\n};\n```\n\n### Version\n\nVersion will be automatically included in all logs.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.version; // 1.0.0\n```\n\n### Error To Object\n\nUtility function to convert an `Error` object to a plain JS object, with all properties preserved. When serialized, `Error` objects will omit custom properties from the output which makes debugging difficult. This utility will traverse all properties in the `Error` object, and output a plain JS object with all properties including the stack trace. See an example below.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\nconst err = new Error(\"error message\");\nconsole.log(err); // \"Error: error message\\n    at Object.<anonymous> …\"\n\nconst errObject = cleLogger.errorToObject(err);\nconsole.log(errObjct); // {name: 'Error', message: 'error message', stack: 'Error: error message\\n    at Object.<anonymous> …'}\n```\n\nExample usage when logging errors:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ntry {\n\t// some code that can throw an error\n} catch (error) {\n\tcleLogger.error(\"My error message\", cleLogger.errorToObject(error));\n}\n```\n\n## Log Object\n\nThe following is the type definition of the log object received by [log sinks](#log-sinks). It consists of properties provided in log messages, as well as some that are automatically inserted like the `eventDate`. You can use this information to customize your logs inside a custom log sink (e.g. filter out unwanted logs, modify some of the properties to include more data, or any kind of conditional logic based on the value of these properties). If using the built in `remoteLogger` log processor with your custom backend, you can expect an array of these objects to be sent to your log endpoint. For more info about implementing a custom logging endpoint see the [remote logging](#remote-logging) section.\n\n```js\n{\n\t// Account sid if provided during init\n\taccountSid: string;\n\n\t// Any additional log data to be included with the log\n\tadditionalData: unknown;\n\n\t// Component name, if provided\n\tcomponentName: string;\n\n\t// Generated or configured token\n\tcorrelationToken: string;\n\n\t// Current UTC datetime in ISO 8601 format\n\teventDate: string;\n\n\t// Log level\n\tlevel: string;\n\n\t// Log code, if provided\n\tlogCode: number;\n\n\t// Log message\n\tmessage: string;\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\tproperties: object;\n\n\t// BrowserFunction if the environment is the browser\n\t// CloudFunction if the environment is Node.js\n\tsource: string;\n\n\t// Indicates that the log contains PII data\n\tcontainsPII: boolean;\n\n\t// Logging library version\n\tloggerVersion: string;\n}\n```\n\n## Handling PII Data\n\nThe logger provides support for helping to protect log messages or data that need to include PII (Personally Identifiable Information). While the logger has several mechanisms that can be used to flag logs that contain PII and block PII from going to particular log sinks, it is still ultimately the developers responsibility to ensure PII is flagged or marked appropriately for the logger to know when it exists - the logger cannot automatically detect if PII is part of a log statement or data object. The general guideline is that developers should avoid including any PII data in log information whenever possible. In the instances where PII data must be included there are several mechanisms the logger supports to help indicate PII data and protect where it is sent.\n\nA `containsPII` parameter exists on log statement methods. This flag should be set to true if there is PII data, or any potential chance of it, in the log message, additional data or custom properties data. When this flag is set to true then it will only allow log sinks that have the `piiSafe` configuration set to true to receive and process the log message. `piiSafe` indicates on a log sink whether that log sink receiver is able to internally store PII data OR has logic to strip PII data that is tagged in the message or from pre-configured additional data fields before the log is written to any storage or output device. When sending data to any `remoteLogger` endpoint the log sink can be marked `piiSafe` only as long as data is appropriately tagged in messages and/or the names of custom properties that might have PII values in additional data are pre-configured AND the endpoint implementer has verified and ensured their receiving code will remove marked PII data immediately before sending logs to any output or storage media.\n\nTo tag any part of the message parameter in a log statement as containing PII within the actual log message use the `tagPII` utility. The `tagPII` utility will indicate to a remote backend that the text between the tags should be stripped or masked before sending logs to any output or storage media.\n\nExample tagged output:\n\n```js\nimport { tagPII } from \"@prftesp/cle-logger\";\n\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\n```\n\n## Advanced Configuration\n\n### Remote logging\n\nThe library provides a built in way to transmit logs to a remote http endpoint using the `remoteLogger` logs processor. The `remoteLogger` can be directed at a known listener such as CLE or you can create your own custom log listener. In addition, the log level in effect can be controlled remotely setting the `minLogLevelOverrideUrl` to an http address that will return the current log level to use instead of any defined default logLevel on each `logSink`. The `minLogLevelOverrideUrl` can point to a known central API such as CLE or you can create your own endpoint.\n\nThe following sections outline how to create your own endpoints for either receiving log messages from the `remoteLogger` log sink or providing the current log level override to it.\n\n**Remote log endpoint**\n\nTo implement your own remote logging endpoint, all you have to do is accept `POST` requests, with a JSON payload of an array of [log objects](#log-object), and return either `{ success: true }` or `{ success: false }`. The endpoint should always return a `200 OK` status even if your server code fails so that the CLE logger does not cause failures in the business logic in which it is embedded. A good use-case would be leveraging a serverless api platform to output logs to a queue.\n\nExample of an Azure Function that can receive requests from the `remoteLogger` log sink:\n\n```js\nmodule.exports = async function(context, req) {\n\tcontext.log(req.body); // body will have the log array\n\n\tcontext.res = {\n\t\tstatus: 200,\n\t\tbody: { success: true },\n\t};\n};\n```\n\n**Remote log level override**\n\nThe `minLogLevelOverrideUrl` configuration property is used to provide an endpoint that accepts `GET` requests and a `accountSid` query string parameter. `accountSid` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the `minLogLevel` configuration property. If null is returned, the `minLogLevel` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a `200 OK` status code and use the `success` property to indicate failure.\n\nExample success response:\n\n```json\n{\n\t\"success\": true,\n\t\"minLogLevel\": \"warn\"\n}\n```\n\nExample error response:\n\n```json\n{\n\t\"success\": false,\n\t\"minLogLevel\": null\n}\n```\n\n## Changelog\n\n### v2.3.0\n\n**New features**\n\n- To address long-running log requests caused by occasional slow remote log responses, this version introduces default timeouts for log requests. Timeout for Twilio event hooks is 2000 ms and is not configurable. Timeout for the `remoteLogger` helper is 500 ms and can be adjusted with an optional second parameter: `remoteLogger(\"https://mylogger.url\", 1000);`\n\n### v2.2.0\n\n**New features**\n\n- Added [alternative logging functions](#alternative-logging-functions) that have a much shorter parameter list, and allow you to specify only those logging properties that you want, without the need to pass in `null` for parameters you are not interested in. New functions are available under `cleLogger.v2` for backward compatibility reasons.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n// if you want to log just the message and the token, \n// you need to default parameters to null until you get to token\ncleLogger.verbose(\"Log message\", null, null, null, null, \"my-token\");\n\n// with the new functions, you just provide an object with the properties you want\ncleLogger.v2.verbose(\"Log message\", { correlationToken: \"my-token\" });\n```\n\n### v2.1.0\n\n**New features**\n\n- Added [Error To Object](#error-to-object) method to assist users in converting error object to plain objects.\n\n### v2.0.7\n\n**New features**\n\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the call sid during a call, and automatically include it in `customProperties` log property for all logs that happen during that call (including Flex Action logs).\n\n### v2.0.5\n\n**New features**\n\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the worker sid and automatically include it in `customProperties` log property.\n\n**Other updates**\n\n- Updated documentation based on user feedback.\n\n### v2.0.4\n\n**Bug fixes**\n\n- Fixed issue with logging to console in Twilio Functions. The `consoleLogger` will now output the log object as serialized json.\n\n### v2.0.0 [2019-12-20]\n\n**New features**\n\n- All logging output is now configured using the `logSinks` property. The library provides two commonly used loggers - `consoleLogger` and a `remoteLogger` that accepts the log url as a parameter. [Configuration](#configuration)\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\n- Log functions have a new optional `containsPII` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\n- A new helper function `tagPII` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\n\n**Breaking changes**\n\n- `customLogSink` configuration renamed to `logSinks`\n- Removed `disableRemoteLogging` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\n- Removed `logToConsole` configuration. This is now configured as a log sink with a `consoleLogger` helper function.\n- Removed `logUrl` configuration. This is now configured as a log sink with a `remoteLogger` helper function.\n- Removed `minLogLevel` configuration. This is now configured per log sink.\n\n**Migration guide**\n\n```js\nimport cleLogger from \"@prftesp/cle-logger\";\n\n// v1.0.0 and older config\ncleLogger.init({\n\tdisableRemoteLogSinks: true, // no direct replacement\n\tlogToConsole: true, // replaced by the \"console logger\" sink\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\n\tcustomLogSinks: [\n\t\t{\n\t\t\tname: \"custom sink\",\n\t\t\tlog: logs => {\n\t\t\t\t/* your custom logging */\n\t\t\t},\n\t\t},\n\t],\n});\n\n// v2.0.0 config\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"custom sink\",\n\t\t\tlog: logs => {\n\t\t\t\t/* your custom logging */\n\t\t\t},\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"console logger\",\n\t\t\tlog: consoleLogger,\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"remote logger\",\n\t\t\tlog: remoteLogger(\"http://log.output/api\"),\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t],\n});\n```\n\n### v1.0.0 [2019-09-12]\n\n- Initial release\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.3.0-beta.20230307.4","_nodeVersion":"18.14.2","_npmVersion":"9.5.0","dist":{"integrity":"sha512-UMORMHIsJBteSuuQLvG9MARoLpGzX2FxPDagjkz53epwxbWiNol1pCzEbfZrn2LfudhcUFit2b/GcnGMcqiWwA==","shasum":"dc769314178fd873cf8fec5122c7d2d51a51258b","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.3.0-beta.20230307.4.tgz","fileCount":17,"unpackedSize":90635,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEN/tuoPwfaHbbqhu8YLFDXMAsKt23o0CWWe/UH/QDz6AiEA/vIJhMmxac8Fv3DpgIw9cl0k1S3NJoYgNoU2zqduXss="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkB60cACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqVVA/9Fljm7FMswXaCG7uVZg75BOEdT36cbxDqcuhBE/HW3qyAvI14\r\n3N7biMt+mjtaSOKNmbwpzKPIA0F/uJ/6YDWfaz9lh1eu3uDaWIfdhLvwJ5j1\r\nnvQIKaysDeDnA+2zD0f7d9J9o+UhjwgI9L+ZShAbRxWPSVXoUCIQRhb8ZvEH\r\nTy8cZzq/LwCkIzhcoQzO/yryhYyV6UGbvEfZe3RSEi1xp6OO0PafRcAw5xAl\r\nVGRz2QuGf8cVeKbdcLko4/TkeRPkDO7nXs3pHL5CQcsYwfhl3YMeLVUameOq\r\nf6BamqD6f79mHKdOaN2b2ml2+/pO8yCdrroC4Kc9XyXRCj4deneWNRMq94Jy\r\nPTt/idouKuCuv1HWdPfEeBqBjNEOk4AhP3nNVouSb3Gi7RHJPrsqu96NFJLg\r\niSE8hDLaoxzo7IWxIVnmlHDbbsSTQxK0HJqnfJmVONCumy6Kh/4MZk7qp0Uq\r\nZb12r2MLPwPk7RymEoCsneRyQu9gi+ax+FOokH7M25lWoj+mbl/sfXi9jZnB\r\nb0PrB1PslUjwQ8ifPs8rp6tWa0PvrUySQ0PMlVypLMlJxjrKAkYSkwr5Co3K\r\n+mtpp9HEONN229oBQNvBU0aMcVE7YicAZx7JWQ7r9UPOfNyM2hJ55oD0U2/r\r\nTJod2x2AAh1TBV/ULi64uYEHLQlS2KW5aDo=\r\n=uK55\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"maintainers":[{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"},{"name":"shelby-prft","email":"shelby.hagman@perficient.com"},{"name":"curtis.swartzentruber","email":"c.swartzentruber@perficient.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.3.0-beta.20230307.4_1678224668652_0.40345986059437355"},"_hasShrinkwrap":false},"2.3.0-beta.20230307.5":{"name":"@prftesp/cle-logger","version":"2.3.0-beta.20230307.5","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* -g && tsc --build && npm run build:es5","build:es5":"rimraf dist-es5/* -g && tsc --build tsconfig.es5.json","test":"jest --runInBand --silent --config jest.config.js","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^29.4.0","@types/node":"^18.14.6","@typescript-eslint/eslint-plugin":"^5.54.1","@typescript-eslint/parser":"^5.54.1","eslint":"^8.35.0","eslint-config-prettier":"^8.7.0","jest":"^29.5.0","prettier":"^2.8.4","rimraf":"^4.3.1","ts-jest":"^29.0.5","typescript":"^4.9.5"},"dependencies":{"axios":"^1.3.4","serializerr":"^1.0.3"},"readme":"﻿CLE Logger\n==\n\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\n\n## Documentation\n\n- [Target Environment](#target-environment)\n- [Simple Example](#simple-example)\n- [Installation](#installation)\n- [Features](#features)\n- [Configuration](#configuration)\n- [API](#api)\n  - [Log sinks](#log-sinks)\n  - [Logging statements](#logging-statements)\n  - [Logging parameters](#logging-parameters)\n  - [Alternative logging functions](#alternative-logging-functions)\n  - [Logging constants](#logging-constants)\n  - [Log codes](#log-codes)\n  - [Registering Twilio Actions](#registering-twilio-actions)\n  - [Twilio Event Hooks](#twilio-event-hooks)\n  - [Version](#version)\n  - [Error To Object](#error-to-object)\n- [Log Object](#log-object)\n- [Handling PII Data](#handling-pii-data)\n- [Advanced Configuration](#advanced-configuration)\n  - [Remote logging](#remote-logging)\n- [Changelog](#changelog)\n\n## Target Environment\n\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\n\n### ES5 Compatible Version\n\nIf you need an ES5 compatible version (for example you need IE11 support), use this package: https://www.npmjs.com/package/@prftesp/cle-logger-es5. **This package is not regularly released with the main library, and should be used only when absolutely necessary**. If you need to support IE11, don't forget to include the necessary polyfills, mainly for `Promise` support. To do this, you can include this script tag on the page, before you load this library `<script src=\"https://cdn.polyfill.io/v3/polyfill.min.js?features=es6\"></script>`, or visit `https://polyfill.io` for more options.\n\n**Please note** that the ES5 version is generated, and is sharing the same code and documentation as the main library. This is why the rest of the documentation doesn't mention the ES5 version in text or examples. The only difference is how you reference it (`@prftesp/cle-logger` vs `@prftesp/cle-logger-es5`). To install it use `npm install @prftesp/cle-logger-es5`, and to import it use `import { cleLogger } from \"@prftesp/cle-logger-es5\";`.\n\n## Simple Example\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"console sink\",\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\tpiiSafe: true,\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n});\n\n// will log message to console\ncleLogger.info(\"log message\");\n```\n\n## Installation\n\n```bash\n$ npm install @prftesp/cle-logger\n```\n\n## Features\n\n- Runs in Node.js and the browser\n- Remote logging\n- Multiple log sinks\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\n- Remote minimum log level overrides\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\n- Support for tagging logs with custom component names or correlation tokens\n- Send Twilio event hook data to a custom backend\n\n## Configuration\n\nTo configure the logger call the `init` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\n\n**Available options**\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// Twilio account sid, required for use with CLE\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\n\n\t// Will be associated with all logs,\n\t// if not provided, a unique uuid will be generated\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\n\n\t// An array of that will receive an array of log objects,\n\t// and can optionally return a promise. Please see the\n\t// \"Log Sinks\" section for more information.\n\tlogSinks: [\n\t\t{\n\t\t\t// name of the log sink\n\t\t\tname: \"exampleConsoleSink\",\n\t\t\t// log handler function\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\t// This flag defines if the log message is processed by this sink when the contains\n\t\t\t// PII parameter of the log statement is true. piiSafe of true indicates all log\n\t\t\t// messages are allowed (even when contains PII is true); piiSafe of false indicates\n\t\t\t// only log messages with contains PII set to false will be processed by this log sink.\n\t\t\t// Defaults to false\n\t\t\tpiiSafe: true,\n\t\t\t// log level for the sink, defaults to INFO\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n\n\t// A way to display additional errors internal to the logger, useful for debugging, defaults to false\n\tlogErrorsToConsole: false,\n\n\t// If logging Twilio Flex Actions, provide a list of actions\n\t// to be excluded from logs\n\texcludedFlexActionNames: [\"SendTyping\"],\n\n\t// A custom endpoint to fetch log level overrides\n\t// NOTE: the provided url should always end with 'accountSid=',\n\t// the library will append the accountSid\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\n\n\t// An instance of ('@twilio/flex-ui').Manager.\n\t// If provided, it will be used to get the worker sid and call sid (only during a call), and\n\t// automatically include it in 'customProperties' log property and with every log\n\ttwilioFlexManager: manager,\n\n\t// To send data from Twilio event hooks, to a custom backend,\n\t// configure one or more endpoints for each event type\n\ttwilioEventHooks: {\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\n\t},\n});\n```\n\n## API\n\nThe logger can be imported as an object in CommonJs or ES6 syntax:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n// or\nconst cleLogger = require(\"@prftesp/cle-logger\");\n```\n\nAlternatively, all top level members of the `cleLogger` are available as named exports and can be imported separately:\n\n```js\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\n\ninit({\n\tlogSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: logLevels.VERBOSE\n\t}],\n});\n\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\n\n// vs\n\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ninit({\n\tcleLogger.logSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: cleLogger.consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}],\n});\n\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\n```\n\n### Log sinks\n\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the `logSinks` config. The logSinks configuration defines an array of objects with each object defining the log sink activity. All provided log sinks will be executed in parallel. A log sink configuration must include the following properties:\n\n- `name`: this is an identifiable name for the logger and must be unique per log sink defined\n- `log`: this indicates the logic to process on each batch of logs processed by the sink. There are several built in processing functions that can be used or you can insert any code inline with this definition. The built in processing functions are `consoleLogger` which will send the log statements to the attached console or `remoteLogger` which will send the log statements to the specified url provided as a definition on the parameter (i.e. `remoteLogger(\"https://mylogger.url\"`). For a custom log processing implementation, the logic must be async operations and must return a Promise object. The custom log method will receive an array of log objects for you to handle. You can find out more about the log object type in the [Log Object](#log-object) section. Here is an example of a custom log function:\n\n```js\nlogs => {\n\t// log each received log to the console separately\n\tlogs.forEach(log => console.log(log));\n\treturn Promise.resolve();\n};\n```\n\n- `logLevel`: defines the minimum default log level that will be processed by this log sink. Log levels lower than the minimum level will not be processed by this log sink. The minimum default log level can be overridden at run time by the value returned from the overall `minLogLevelOverrideUrl` that is set up in the library `init`.\n- `piiSafe`: indicates if log messages with a `containsPII` parameter value of true should be processed by the log sink. If `containsPII` parameter value on the log message is true and piiSafe is false then the log message will not be processed by this log sink, if piiSafe is true then it will. If the `containsPII` parameter value is false on the log message than it is always processed by the log sink regardless of the piiSafe value.\n\nCustom log sink configuration example. In this example, the custom code executed for each log statement is to append the log into a file. Note how a Promise is returned as required:\n\n```js\n{\n\t// name of the log sink\n\tname: \"exampleConsoleSink\",\n\t// log handler function - this is where you put your custom handling code and\n\t// remember to always return a promise. If your operation is not asynchronous,\n\t// you can do this by returning Promise.resolve().\n\tlog: logs => {\n\t\tfs.appendFile(\n\t\t\t\"./log.txt\",\n\t\t\t`${JSON.stringify(logs)}`,\n\t\t\tconsole.log\n\t\t);\n\t\treturn Promise.resolve();\n\t},\n\t// should this log sink accept PII data, defaults to false\n\tpiiSafe: true,\n\t// log level for the sink, defaults to INFO\n\tlogLevel: cleLogger.logLevels.VERBOSE\n}\n```\n\nExample log sink configuration that uses a custom coded sink and both standard console and remote logging sinks:\n\n```js\nconst fs = require(\"fs\");\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"fileSink\",\n\t\t\tlog: logs => {\n\t\t\t\tfs.appendFile(\"./log.txt\", `${JSON.stringify(logs)}`, console.log);\n\t\t\t\treturn Promise.resolve();\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"remoteSink\",\n\t\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\n\t\t\tlogLevel: cleLogger.logLevels.WARN,\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"consoleSink\",\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n});\n```\n\n### Logging statements\n\nTo create logs, you can use one of the logging function available. They all take the same arguments, described below, and log at a corresponding log level. For example, if you use the `cleLogger.verbose` logging function, and your log sink is configured with `logLevel: cleLogger.logLevels.VERBOSE`, that log sink will receive the log. If your log sink is configured with a higher log level, for example `logLevel: cleLogger.logLevels.INFO`, then it would not receive the verbose log.\n\nHere are the available logging functions:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(...);\ncleLogger.info(...);\ncleLogger.warn(...);\ncleLogger.error(...);\ncleLogger.critical(...);\n```\n\nThe functions are all async, return `Promise<void>`, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\n\n### Logging parameters\n\nAll logging functions take the same parameters:\n\n- `message`: the only required property, no logs will be generated without it\n  - type: string\n  - required: yes\n- `additionalData`: any additional log data you would like to include with the log, can be any data type\n  - type: any\n  - required: no\n  - default value: null\n- `customProperties`: a single level deep object of important properties you would like to distinguish from general log data, like various sids CLE uses it to index these properties in Application Insights. For CLE, try to use keys found in `cleLogger.customPropertyKeys`\n  - type: object\n  - required: no\n  - default value: {}\n- `componentName`: another useful way to 'tag' your logs for easier searching. Can be any string, but for CLE, try to use available options in `cleLogger.componentNames`\n  - type: string\n  - required: no\n  - default value: \"\"\n- `logCode`: log code, can be any number used to categorize logs. For defined ranges see the [log codes](#log-codes) section.\n  - type: number\n  - required: no\n  - default value: 0\n- `correlationToken`: correlation token to be associated with this log only\n  - type: string\n  - required: no\n  - default value: token provided in `init`, or a generated token if one wasn't provided\n- `containsPII`: does the log contain PII data, defaults to false. If set to true, the log will be passed in only to those log sinks that have `piiSafe` set to true. For more info see the [handling PII data](#handling-pii-data) section.\n  - type: boolean\n  - required: no\n  - default value: false\n\n### Alternative logging functions\n\nIf you would prefer to pass in an object to logging functions, instead of using a long list of parameters, you can use the new logging functions available under the `cleLogger.v2` namespace. These functions are namespaced to keep backwards compatibility, but you should feel free to use them going forward. The behavior of these functions is exactly the same as with those listed in [logging statements](#logging-statements) section.\n\nExample verbose logging statement with both versions of the logging function:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(\n\t\"Log message\", // message\n\t{ data: \"test\" }, // additionalData\n\t{ [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" }, // customProperties\n\tcleLogger.componentNames.SESSION, // componentName\n\t70123, // logCode\n\t\"my-token\", // correlationToken\n\ttrue, // containsPII\n);\n\ncleLogger.v2.verbose(\n\t\"Log message\",\n\t{\n\t\tadditionalData: { data: \"test\" },\n\t\tcustomProperties: { [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" },\n\t\tcomponentName: cleLogger.componentNames.SESSION,\n\t\tlogCode: 70123,\n\t\tcorrelationToken: \"my-token\",\n\t\tcontainsPII: true,\n\t}\n);\n```\n\nAs you can see in the examples above, both functions take in the same values, but the `v2` version makes it a bit easier to see which value corresponds to each key. It comes down to personal preference, use whichever you prefer.\n\n### Logging constants\n\n**Logging levels**\n\nLogging level constants should be used when configuring the log level of a [log sink](#log-sinks).\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.logLevels.VERBOSE; // verbose\ncleLogger.logLevels.INFO; // info\ncleLogger.logLevels.WARN; // warn\ncleLogger.logLevels.ERROR; // error\ncleLogger.logLevels.CRITICAL; // critical\n```\n\n**Component names**\n\nDefines a set of commonly used components that should be used for the `componentName` parameter value of a log statement if you are logging from one of these common components in a Twilio implementation.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.componentNames.SESSION;\ncleLogger.componentNames.DIALER;\ncleLogger.componentNames.DASHBOARD;\ncleLogger.componentNames.AFTERCALL;\ncleLogger.componentNames.SUPERVISOR;\n```\n\n**Custom property keys**\n\nDefines a set of commonly used key that should be used when defining the custom properties object that is supplied in the `customProperties` parameter of any logging statement.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.customPropertyKeys.CHANNEL_SID;\ncleLogger.customPropertyKeys.TASK_SID;\ncleLogger.customPropertyKeys.CALL_SID;\ncleLogger.customPropertyKeys.WORKFLOW_SID;\ncleLogger.customPropertyKeys.WORKER_SID;\n```\n\n### Log codes\n\nIt can be helpful when investigating issues to only view logs related to specific areas of a system. Use Log Codes to give a category definition for what area of the contact center the log activity occurred within. Use numbers within the following pre-defined log code ranges or define custom log codes by using your own numbers higher than 300,000.\n\n- 100 000 - 109 999 => CALL_STATUS\n- 110 000 - 119 999 => IVR\n- 120 000 - 129 999 => ROUTING\n- 130 000 - 139 999 => AGENT_ACTION\n- 140 000 - 149 999 => TRANSFER\n- 150 000 - 159 999 => RM_INTEGRATION\n- 160 000 - 169 999 => WFM_INTEGRATION\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\n\n### Registering Twilio Actions\n\nIt is important to log information from the Twilio Flex UI for all implementations even when there is little customization happening. Many developers find it useful to generate log activity when some, or any, of the Twilio Actions occur within the Twilio Flex UI. Twilio Actions are events that arise from the Twilio Flex UI base implementation and indicate when certain activity is occurring - such as an agent hanging up a call. The CLE Logger library can be setup to automatically register all, or selected, Twilio Actions such that right after any code tied to the Action event has been executed a log statement is written indicating the Action occurred. This log statement will also automatically include the action event payload. To enable this logging capability, use the following approach to register the Twilio Actions. This only needs to be performed once during the initialization of the Twilio Flex UI page on the client.\n\nExample in a Twilio Flex UI plugin:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n/**\n * @param flex { typeof import('@twilio/flex-ui') }\n * @param manager { import('@twilio/flex-ui').Manager }\n */\ninit(flex, manager) {\n\tcleLogger.init({\n\t\taccountSid: \"your-account-sid\"\n\t});\n\n\tcleLogger.registerTwilioActions(flex);\n}\n```\n\n**Note**: A Twilio account sid must be provided during `init` with the `accountSid` parameter, otherwise the actions won't be logged.\n\nThe logger will loop through all actions found under `flex.Actions.actions`, and attach `after` event handlers to log the action name and payload. Any private properties found in the payload (starting with '\\_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Not all Action events need to be logged and by using the `excludedFlexActionNames` property in the logger configuration the Action events not desired can be ignored. Any actions names configured in `excludedFlexActionNames` will not be included in the logs.\n\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\n\n**Twilio Actions log levels**\n\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of `INFO`.\n\n```js\nAcceptTask: INFO;\nCancelTransfer: INFO;\nCompleteTask: INFO;\nHangupCall: INFO;\nHideDirectory: VERBOSE;\nHistoryGo: VERBOSE;\nHistoryGoBack: VERBOSE;\nHistoryGoForward: VERBOSE;\nHistoryPush: VERBOSE;\nHistoryReplace: VERBOSE;\nHoldCall: INFO;\nHoldParticipant: INFO;\nKickParticipant: INFO;\nLogout: INFO;\nMonitorCall: INFO;\nNavigateToView: INFO;\nRejectTask: INFO;\nSelectTask: INFO;\nSelectTaskInSupervisor: INFO;\nSelectWorkerInSupervisor: INFO;\nSendMessage: INFO;\nSendTyping: VERBOSE;\nSetActivity: INFO;\nSetInputText: VERBOSE;\nShowDirectory: VERBOSE;\nStopMonitoringCall: INFO;\nToggleMute: INFO;\nToggleSidebar: VERBOSE;\nTransferTask: INFO;\nUnholdCall: INFO;\nUnholdParticipant: INFO;\nWrapupTask: INFO;\n```\n\n### Twilio Event Hooks\n\nCurrently, the Twilio console only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with `twilioEventHooks` option, and use the following functions to send event data to a custom endpoint.\n\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is `application/x-www-form-urlencoded` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// unrelated init properties omitted from this example.\n\t// include in the twilioEventHooks array an entry for only the event hooks\n\t// the executing code is going to relay. For example, if the code is only\n\t// handling task router web hook events then you would only need to define\n\t// the taskRouter relay url here.\n\ttwilioEventHooks: {\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\n\t},\n});\n\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\n```\n\nExample Azure Function hook for debugger events:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\nmodule.exports = async function(context, req) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\t},\n\t});\n\n\t// log to console\n\tcleLogger.info(\"Received Twilio debugger event\");\n\n\t// send event data to a configured url\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\n};\n```\n\n**NOTE WHEN USING THIS FUNCTIONALITY IN TWILIO FUNCTIONS:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to `x-www-form-urlencoded` format.\n\nExample Twilio function task router relay using the `form-urlencoded` npm package to convert the data to `x-www-form-urlencoded` format:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\nconst formurlencoded = require(\"form-urlencoded\").default;\n\nexports.handler = async function(context, event, callback) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\t},\n\t});\n\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\n\n\tcallback();\n};\n```\n\n### Version\n\nVersion will be automatically included in all logs.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.version; // 1.0.0\n```\n\n### Error To Object\n\nUtility function to convert an `Error` object to a plain JS object, with all properties preserved. When serialized, `Error` objects will omit custom properties from the output which makes debugging difficult. This utility will traverse all properties in the `Error` object, and output a plain JS object with all properties including the stack trace. See an example below.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\nconst err = new Error(\"error message\");\nconsole.log(err); // \"Error: error message\\n    at Object.<anonymous> …\"\n\nconst errObject = cleLogger.errorToObject(err);\nconsole.log(errObjct); // {name: 'Error', message: 'error message', stack: 'Error: error message\\n    at Object.<anonymous> …'}\n```\n\nExample usage when logging errors:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ntry {\n\t// some code that can throw an error\n} catch (error) {\n\tcleLogger.error(\"My error message\", cleLogger.errorToObject(error));\n}\n```\n\n## Log Object\n\nThe following is the type definition of the log object received by [log sinks](#log-sinks). It consists of properties provided in log messages, as well as some that are automatically inserted like the `eventDate`. You can use this information to customize your logs inside a custom log sink (e.g. filter out unwanted logs, modify some of the properties to include more data, or any kind of conditional logic based on the value of these properties). If using the built in `remoteLogger` log processor with your custom backend, you can expect an array of these objects to be sent to your log endpoint. For more info about implementing a custom logging endpoint see the [remote logging](#remote-logging) section.\n\n```js\n{\n\t// Account sid if provided during init\n\taccountSid: string;\n\n\t// Any additional log data to be included with the log\n\tadditionalData: unknown;\n\n\t// Component name, if provided\n\tcomponentName: string;\n\n\t// Generated or configured token\n\tcorrelationToken: string;\n\n\t// Current UTC datetime in ISO 8601 format\n\teventDate: string;\n\n\t// Log level\n\tlevel: string;\n\n\t// Log code, if provided\n\tlogCode: number;\n\n\t// Log message\n\tmessage: string;\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\tproperties: object;\n\n\t// BrowserFunction if the environment is the browser\n\t// CloudFunction if the environment is Node.js\n\tsource: string;\n\n\t// Indicates that the log contains PII data\n\tcontainsPII: boolean;\n\n\t// Logging library version\n\tloggerVersion: string;\n}\n```\n\n## Handling PII Data\n\nThe logger provides support for helping to protect log messages or data that need to include PII (Personally Identifiable Information). While the logger has several mechanisms that can be used to flag logs that contain PII and block PII from going to particular log sinks, it is still ultimately the developers responsibility to ensure PII is flagged or marked appropriately for the logger to know when it exists - the logger cannot automatically detect if PII is part of a log statement or data object. The general guideline is that developers should avoid including any PII data in log information whenever possible. In the instances where PII data must be included there are several mechanisms the logger supports to help indicate PII data and protect where it is sent.\n\nA `containsPII` parameter exists on log statement methods. This flag should be set to true if there is PII data, or any potential chance of it, in the log message, additional data or custom properties data. When this flag is set to true then it will only allow log sinks that have the `piiSafe` configuration set to true to receive and process the log message. `piiSafe` indicates on a log sink whether that log sink receiver is able to internally store PII data OR has logic to strip PII data that is tagged in the message or from pre-configured additional data fields before the log is written to any storage or output device. When sending data to any `remoteLogger` endpoint the log sink can be marked `piiSafe` only as long as data is appropriately tagged in messages and/or the names of custom properties that might have PII values in additional data are pre-configured AND the endpoint implementer has verified and ensured their receiving code will remove marked PII data immediately before sending logs to any output or storage media.\n\nTo tag any part of the message parameter in a log statement as containing PII within the actual log message use the `tagPII` utility. The `tagPII` utility will indicate to a remote backend that the text between the tags should be stripped or masked before sending logs to any output or storage media.\n\nExample tagged output:\n\n```js\nimport { tagPII } from \"@prftesp/cle-logger\";\n\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\n```\n\n## Advanced Configuration\n\n### Remote logging\n\nThe library provides a built in way to transmit logs to a remote http endpoint using the `remoteLogger` logs processor. The `remoteLogger` can be directed at a known listener such as CLE or you can create your own custom log listener. In addition, the log level in effect can be controlled remotely setting the `minLogLevelOverrideUrl` to an http address that will return the current log level to use instead of any defined default logLevel on each `logSink`. The `minLogLevelOverrideUrl` can point to a known central API such as CLE or you can create your own endpoint.\n\nThe following sections outline how to create your own endpoints for either receiving log messages from the `remoteLogger` log sink or providing the current log level override to it.\n\n**Remote log endpoint**\n\nTo implement your own remote logging endpoint, all you have to do is accept `POST` requests, with a JSON payload of an array of [log objects](#log-object), and return either `{ success: true }` or `{ success: false }`. The endpoint should always return a `200 OK` status even if your server code fails so that the CLE logger does not cause failures in the business logic in which it is embedded. A good use-case would be leveraging a serverless api platform to output logs to a queue.\n\nExample of an Azure Function that can receive requests from the `remoteLogger` log sink:\n\n```js\nmodule.exports = async function(context, req) {\n\tcontext.log(req.body); // body will have the log array\n\n\tcontext.res = {\n\t\tstatus: 200,\n\t\tbody: { success: true },\n\t};\n};\n```\n\n**Remote log level override**\n\nThe `minLogLevelOverrideUrl` configuration property is used to provide an endpoint that accepts `GET` requests and a `accountSid` query string parameter. `accountSid` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the `minLogLevel` configuration property. If null is returned, the `minLogLevel` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a `200 OK` status code and use the `success` property to indicate failure.\n\nExample success response:\n\n```json\n{\n\t\"success\": true,\n\t\"minLogLevel\": \"warn\"\n}\n```\n\nExample error response:\n\n```json\n{\n\t\"success\": false,\n\t\"minLogLevel\": null\n}\n```\n\n## Changelog\n\n### v2.3.0\n\n**New features**\n\n- To address long-running log requests caused by occasional slow remote log responses, this version introduces default timeouts for log requests. Timeout for Twilio event hooks is 2000 ms and is not configurable. Timeout for the `remoteLogger` helper is 500 ms and can be adjusted with an optional second parameter: `remoteLogger(\"https://mylogger.url\", 1000);`\n\n### v2.2.0\n\n**New features**\n\n- Added [alternative logging functions](#alternative-logging-functions) that have a much shorter parameter list, and allow you to specify only those logging properties that you want, without the need to pass in `null` for parameters you are not interested in. New functions are available under `cleLogger.v2` for backward compatibility reasons.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n// if you want to log just the message and the token, \n// you need to default parameters to null until you get to token\ncleLogger.verbose(\"Log message\", null, null, null, null, \"my-token\");\n\n// with the new functions, you just provide an object with the properties you want\ncleLogger.v2.verbose(\"Log message\", { correlationToken: \"my-token\" });\n```\n\n### v2.1.0\n\n**New features**\n\n- Added [Error To Object](#error-to-object) method to assist users in converting error object to plain objects.\n\n### v2.0.7\n\n**New features**\n\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the call sid during a call, and automatically include it in `customProperties` log property for all logs that happen during that call (including Flex Action logs).\n\n### v2.0.5\n\n**New features**\n\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the worker sid and automatically include it in `customProperties` log property.\n\n**Other updates**\n\n- Updated documentation based on user feedback.\n\n### v2.0.4\n\n**Bug fixes**\n\n- Fixed issue with logging to console in Twilio Functions. The `consoleLogger` will now output the log object as serialized json.\n\n### v2.0.0 [2019-12-20]\n\n**New features**\n\n- All logging output is now configured using the `logSinks` property. The library provides two commonly used loggers - `consoleLogger` and a `remoteLogger` that accepts the log url as a parameter. [Configuration](#configuration)\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\n- Log functions have a new optional `containsPII` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\n- A new helper function `tagPII` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\n\n**Breaking changes**\n\n- `customLogSink` configuration renamed to `logSinks`\n- Removed `disableRemoteLogging` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\n- Removed `logToConsole` configuration. This is now configured as a log sink with a `consoleLogger` helper function.\n- Removed `logUrl` configuration. This is now configured as a log sink with a `remoteLogger` helper function.\n- Removed `minLogLevel` configuration. This is now configured per log sink.\n\n**Migration guide**\n\n```js\nimport cleLogger from \"@prftesp/cle-logger\";\n\n// v1.0.0 and older config\ncleLogger.init({\n\tdisableRemoteLogSinks: true, // no direct replacement\n\tlogToConsole: true, // replaced by the \"console logger\" sink\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\n\tcustomLogSinks: [\n\t\t{\n\t\t\tname: \"custom sink\",\n\t\t\tlog: logs => {\n\t\t\t\t/* your custom logging */\n\t\t\t},\n\t\t},\n\t],\n});\n\n// v2.0.0 config\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"custom sink\",\n\t\t\tlog: logs => {\n\t\t\t\t/* your custom logging */\n\t\t\t},\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"console logger\",\n\t\t\tlog: consoleLogger,\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"remote logger\",\n\t\t\tlog: remoteLogger(\"http://log.output/api\"),\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t],\n});\n```\n\n### v1.0.0 [2019-09-12]\n\n- Initial release\n","readmeFilename":"README.md","_id":"@prftesp/cle-logger@2.3.0-beta.20230307.5","_nodeVersion":"18.14.2","_npmVersion":"9.5.0","dist":{"integrity":"sha512-aqqwLuzR+4RvUynuTM9b6hZpkelQX9CIYMkA8yTKPkavmr+GUL4t4JKGVdoyDnzKBhkN+1bXlTnjnF5Sn3Pl6A==","shasum":"4c9f5a5b7362397993a2b880da60092fbc0377c7","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.3.0-beta.20230307.5.tgz","fileCount":17,"unpackedSize":90635,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICdYBVu0N74ip0w/AN+oy/N7sRm9DlGElEASAvuTUvbuAiEAl1DSN5OL/WvrfprxG6I4zQa1R3uUQxPHWMDlkNfL9A0="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkB67DACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqV7xAAgb+9+rDs2l1JkKmn/fYMXfdU+CcfpSO2XcEuBghLPGMFM6mS\r\nnIKaaiUuJuFRvtFG/2sYDHXULHhBSkQ0J2xXNp7jnVOWbCgdrTeX9RtWtedS\r\nxSZYxtcwTfT/bBx3bOJlcdkg0RT6hLCf5ZwGqfBUhHfLcBy43DxbzgblV4iM\r\nmBqXTafobF3/YyfKFew+VRSXlRCVD0YiIfxoSNXQogcMXEPZlIm/P2s4zj/z\r\nV4PtShFI/7caALl4WgJLXDHJVxz2EZsrCPBqXMtZlLH5g6OqxoKafZaavQaN\r\n9TQ2VSH+aTMfye4k28TRl6rACYHpHcSJgUa1aSFKDeREMYEtIfELo6VVlA1M\r\nM/vLlwLVYAeOeQAq6/TqMxsWe+jb7mfIQBrQCHgpR+HGJu1zNkZjolgRoYkC\r\nLspEtIMONUmBwhmBb8THsb1g9p4zE3sQrkw8LQwQPwCTIsPMPew57kcpHsdj\r\nxEV+zKLGLiERo6Doxu//xAk4FJJe9fJrBObcvBRzPsSHts7oGmXkn6/VtZiF\r\nYYDeg9naC3HGiV+rWsTHo6GazkxLotbMXjt4+7SWhTJ5TNHEsCRF7AQtEa7H\r\nDaZh6FyQpW3xx+tM5x1oa1V6VEjsh7SAPXrCK7vaF5LZOsjiHiWG3hi8SOxI\r\nQpcdwHZltzsYhY0jZz924UtoT6ikPk+qk6Y=\r\n=8PRf\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"maintainers":[{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"},{"name":"shelby-prft","email":"shelby.hagman@perficient.com"},{"name":"curtis.swartzentruber","email":"c.swartzentruber@perficient.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.3.0-beta.20230307.5_1678225090985_0.018793180031967616"},"_hasShrinkwrap":false},"2.3.0":{"name":"@prftesp/cle-logger","version":"2.3.0","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* -g && tsc --build && npm run build:es5","build:es5":"rimraf dist-es5/* -g && tsc --build tsconfig.es5.json","test":"jest --runInBand --silent --config jest.config.js","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^29.4.0","@types/node":"^18.14.6","@typescript-eslint/eslint-plugin":"^5.54.1","@typescript-eslint/parser":"^5.54.1","eslint":"^8.35.0","eslint-config-prettier":"^8.7.0","jest":"^29.5.0","prettier":"^2.8.4","rimraf":"^4.3.1","ts-jest":"^29.0.5","typescript":"^4.9.5"},"dependencies":{"axios":"^1.3.4","serializerr":"^1.0.3"},"_id":"@prftesp/cle-logger@2.3.0","_nodeVersion":"18.14.2","_npmVersion":"9.5.0","dist":{"integrity":"sha512-j/Jx/i5brnZwMym9FhtWlHbKyOi7bMz0vTAtIGcmO6jLzJwk0vuEYRKU5aBCse56joatt5hCe0Fy3MzrHTjX2A==","shasum":"0152176726d843d15ded9f4bd3a527505ed6e08f","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.3.0.tgz","fileCount":17,"unpackedSize":90534,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDPoG9rmRhZHJ2gySTF15UlDgU0mQbsTH9CrKNCTzrQaAIgQWTe14pBVmyma4S+F8RpMsDqkl4uBWEeg0PjexJYc98="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJkB68uACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmomIQ/+OVKnoaa4+b/Plyt9P03TA6k3BC3PvBlcrAYGIvspVGQ51MrQ\r\nWGoagrpEVeY2tkxBmtOmdRcoABD29GKdIcVnHp+MPjmR+BOtAMexasEuYJKm\r\nenoouGBVh7p5t5BjEwJN5WmF4DL6wrfccgJ3T1R7A/aC8za1R61Xh1RiOqzr\r\nNkqZ2Bx5Zqz46wtX1PnrZjg/Fc937ODw8vXIFuxFd6tSwn0qsbbyW68IS4zX\r\netcyMgxgTDgeqYjoKjSUkXkfj+rNXkNp2YWfycGP6TGkIjsIOagrV6o1k4R4\r\nJjApf8zTtbRuYnYqOm4NnvnLKhmjBjqwNJoxfWBW5iPmUsKSiedBXM8u5DXj\r\n+Yh1o0jVT5tD8wDngiVyz8NAQs3/L+GBMU5yIRPi+nz3sciX8pWHPw8H9Y80\r\nYpuR6JqxoFLQzc4z7vYs2DStVHvlEM8w5Uh/EGChNEbDTfbU6S7uU2H2+y/G\r\nHybGRyHYravrp3dZs7xZ+0u9NFy9Kzt0vUB/8VSKtqQYt3G3R8atp2B8BxdK\r\nQiEr94ICiqY6Iljzp6mYQlEjydt4IiBwbnwOs7hZLp0MJLOnn5hPmEK9pWJL\r\nDFVqr485xane2u4ZRuydqkIA/2CXzu9oVaAmIN9jKKoRmifzZv9YopKRaOKb\r\nh0RjsAwpYxJy8JhRp7tEwZ8xaqLzDdwHz3M=\r\n=tNsC\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"maintainers":[{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"},{"name":"shelby-prft","email":"shelby.hagman@perficient.com"},{"name":"curtis.swartzentruber","email":"c.swartzentruber@perficient.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.3.0_1678225198236_0.3449663242066614"},"_hasShrinkwrap":false},"2.3.1-beta.20240131.2":{"name":"@prftesp/cle-logger","version":"2.3.1-beta.20240131.2","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* -g && tsc --build && npm run build:es5","build:es5":"rimraf dist-es5/* -g && tsc --build tsconfig.es5.json","test":"jest --runInBand --silent --config jest.config.js","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^29.4.0","@types/node":"^18.14.6","@typescript-eslint/eslint-plugin":"^5.54.1","@typescript-eslint/parser":"^5.54.1","eslint":"^8.35.0","eslint-config-prettier":"^8.7.0","jest":"^29.5.0","prettier":"^2.8.4","rimraf":"^4.3.1","ts-jest":"^29.0.5","typescript":"^4.9.5"},"dependencies":{"axios":"^1.3.4","serializerr":"^1.0.3"},"_id":"@prftesp/cle-logger@2.3.1-beta.20240131.2","readme":"﻿CLE Logger\n==\n\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\n\n## Documentation\n\n- [Target Environment](#target-environment)\n- [Simple Example](#simple-example)\n- [Installation](#installation)\n- [Features](#features)\n- [Configuration](#configuration)\n- [API](#api)\n  - [Log sinks](#log-sinks)\n  - [Logging statements](#logging-statements)\n  - [Logging parameters](#logging-parameters)\n  - [Alternative logging functions](#alternative-logging-functions)\n  - [Logging constants](#logging-constants)\n  - [Log codes](#log-codes)\n  - [Registering Twilio Actions](#registering-twilio-actions)\n  - [Twilio Event Hooks](#twilio-event-hooks)\n  - [Version](#version)\n  - [Error To Object](#error-to-object)\n- [Log Object](#log-object)\n- [Handling PII Data](#handling-pii-data)\n- [Advanced Configuration](#advanced-configuration)\n  - [Remote logging](#remote-logging)\n- [Changelog](#changelog)\n\n## Target Environment\n\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\n\n### ES5 Compatible Version\n\nIf you need an ES5 compatible version (for example you need IE11 support), use this package: https://www.npmjs.com/package/@prftesp/cle-logger-es5. **This package is not regularly released with the main library, and should be used only when absolutely necessary**. If you need to support IE11, don't forget to include the necessary polyfills, mainly for `Promise` support. To do this, you can include this script tag on the page, before you load this library `<script src=\"https://cdn.polyfill.io/v3/polyfill.min.js?features=es6\"></script>`, or visit `https://polyfill.io` for more options.\n\n**Please note** that the ES5 version is generated, and is sharing the same code and documentation as the main library. This is why the rest of the documentation doesn't mention the ES5 version in text or examples. The only difference is how you reference it (`@prftesp/cle-logger` vs `@prftesp/cle-logger-es5`). To install it use `npm install @prftesp/cle-logger-es5`, and to import it use `import { cleLogger } from \"@prftesp/cle-logger-es5\";`.\n\n## Simple Example\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"console sink\",\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\tpiiSafe: true,\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n});\n\n// will log message to console\ncleLogger.info(\"log message\");\n```\n\n## Installation\n\n```bash\n$ npm install @prftesp/cle-logger\n```\n\n## Features\n\n- Runs in Node.js and the browser\n- Remote logging\n- Multiple log sinks\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\n- Remote minimum log level overrides\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\n- Support for tagging logs with custom component names or correlation tokens\n- Send Twilio event hook data to a custom backend\n\n## Configuration\n\nTo configure the logger call the `init` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\n\n**Available options**\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// Twilio account sid, required for use with CLE\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\n\n\t// Will be associated with all logs,\n\t// if not provided, a unique uuid will be generated\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\n\n\t// An array of that will receive an array of log objects,\n\t// and can optionally return a promise. Please see the\n\t// \"Log Sinks\" section for more information.\n\tlogSinks: [\n\t\t{\n\t\t\t// name of the log sink\n\t\t\tname: \"exampleConsoleSink\",\n\t\t\t// log handler function\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\t// This flag defines if the log message is processed by this sink when the contains\n\t\t\t// PII parameter of the log statement is true. piiSafe of true indicates all log\n\t\t\t// messages are allowed (even when contains PII is true); piiSafe of false indicates\n\t\t\t// only log messages with contains PII set to false will be processed by this log sink.\n\t\t\t// Defaults to false\n\t\t\tpiiSafe: true,\n\t\t\t// log level for the sink, defaults to INFO\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n\n\t// A way to display additional errors internal to the logger, useful for debugging, defaults to false\n\tlogErrorsToConsole: false,\n\n\t// If logging Twilio Flex Actions, provide a list of actions\n\t// to be excluded from logs\n\texcludedFlexActionNames: [\"SendTyping\"],\n\n\t// A custom endpoint to fetch log level overrides\n\t// NOTE: the provided url should always end with 'accountSid=',\n\t// the library will append the accountSid\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\n\n\t// An instance of ('@twilio/flex-ui').Manager.\n\t// If provided, it will be used to get the worker sid and call sid (only during a call), and\n\t// automatically include it in 'customProperties' log property and with every log\n\ttwilioFlexManager: manager,\n\n\t// To send data from Twilio event hooks, to a custom backend,\n\t// configure one or more endpoints for each event type\n\ttwilioEventHooks: {\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\n\t},\n});\n```\n\n## API\n\nThe logger can be imported as an object in CommonJs or ES6 syntax:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n// or\nconst cleLogger = require(\"@prftesp/cle-logger\");\n```\n\nAlternatively, all top level members of the `cleLogger` are available as named exports and can be imported separately:\n\n```js\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\n\ninit({\n\tlogSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: logLevels.VERBOSE\n\t}],\n});\n\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\n\n// vs\n\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ninit({\n\tcleLogger.logSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: cleLogger.consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}],\n});\n\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\n```\n\n### Log sinks\n\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the `logSinks` config. The logSinks configuration defines an array of objects with each object defining the log sink activity. All provided log sinks will be executed in parallel. A log sink configuration must include the following properties:\n\n- `name`: this is an identifiable name for the logger and must be unique per log sink defined\n- `log`: this indicates the logic to process on each batch of logs processed by the sink. There are several built in processing functions that can be used or you can insert any code inline with this definition. The built in processing functions are `consoleLogger` which will send the log statements to the attached console or `remoteLogger` which will send the log statements to the specified url provided as a definition on the parameter (i.e. `remoteLogger(\"https://mylogger.url\"`). For a custom log processing implementation, the logic must be async operations and must return a Promise object. The custom log method will receive an array of log objects for you to handle. You can find out more about the log object type in the [Log Object](#log-object) section. Here is an example of a custom log function:\n\n```js\nlogs => {\n\t// log each received log to the console separately\n\tlogs.forEach(log => console.log(log));\n\treturn Promise.resolve();\n};\n```\n\n- `logLevel`: defines the minimum default log level that will be processed by this log sink. Log levels lower than the minimum level will not be processed by this log sink. The minimum default log level can be overridden at run time by the value returned from the overall `minLogLevelOverrideUrl` that is set up in the library `init`.\n- `piiSafe`: indicates if log messages with a `containsPII` parameter value of true should be processed by the log sink. If `containsPII` parameter value on the log message is true and piiSafe is false then the log message will not be processed by this log sink, if piiSafe is true then it will. If the `containsPII` parameter value is false on the log message than it is always processed by the log sink regardless of the piiSafe value.\n\nCustom log sink configuration example. In this example, the custom code executed for each log statement is to append the log into a file. Note how a Promise is returned as required:\n\n```js\n{\n\t// name of the log sink\n\tname: \"exampleConsoleSink\",\n\t// log handler function - this is where you put your custom handling code and\n\t// remember to always return a promise. If your operation is not asynchronous,\n\t// you can do this by returning Promise.resolve().\n\tlog: logs => {\n\t\tfs.appendFile(\n\t\t\t\"./log.txt\",\n\t\t\t`${JSON.stringify(logs)}`,\n\t\t\tconsole.log\n\t\t);\n\t\treturn Promise.resolve();\n\t},\n\t// should this log sink accept PII data, defaults to false\n\tpiiSafe: true,\n\t// log level for the sink, defaults to INFO\n\tlogLevel: cleLogger.logLevels.VERBOSE\n}\n```\n\nExample log sink configuration that uses a custom coded sink and both standard console and remote logging sinks:\n\n```js\nconst fs = require(\"fs\");\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"fileSink\",\n\t\t\tlog: logs => {\n\t\t\t\tfs.appendFile(\"./log.txt\", `${JSON.stringify(logs)}`, console.log);\n\t\t\t\treturn Promise.resolve();\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"remoteSink\",\n\t\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\n\t\t\tlogLevel: cleLogger.logLevels.WARN,\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"consoleSink\",\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n});\n```\n\n### Logging statements\n\nTo create logs, you can use one of the logging function available. They all take the same arguments, described below, and log at a corresponding log level. For example, if you use the `cleLogger.verbose` logging function, and your log sink is configured with `logLevel: cleLogger.logLevels.VERBOSE`, that log sink will receive the log. If your log sink is configured with a higher log level, for example `logLevel: cleLogger.logLevels.INFO`, then it would not receive the verbose log.\n\nHere are the available logging functions:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(...);\ncleLogger.info(...);\ncleLogger.warn(...);\ncleLogger.error(...);\ncleLogger.critical(...);\n```\n\nThe functions are all async, return `Promise<void>`, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\n\n### Logging parameters\n\nAll logging functions take the same parameters:\n\n- `message`: the only required property, no logs will be generated without it\n  - type: string\n  - required: yes\n- `additionalData`: any additional log data you would like to include with the log, can be any data type\n  - type: any\n  - required: no\n  - default value: null\n- `customProperties`: a single level deep object of important properties you would like to distinguish from general log data, like various sids CLE uses it to index these properties in Application Insights. For CLE, try to use keys found in `cleLogger.customPropertyKeys`\n  - type: object\n  - required: no\n  - default value: {}\n- `componentName`: another useful way to 'tag' your logs for easier searching. Can be any string, but for CLE, try to use available options in `cleLogger.componentNames`\n  - type: string\n  - required: no\n  - default value: \"\"\n- `logCode`: log code, can be any number used to categorize logs. For defined ranges see the [log codes](#log-codes) section.\n  - type: number\n  - required: no\n  - default value: 0\n- `correlationToken`: correlation token to be associated with this log only\n  - type: string\n  - required: no\n  - default value: token provided in `init`, or a generated token if one wasn't provided\n- `containsPII`: does the log contain PII data, defaults to false. If set to true, the log will be passed in only to those log sinks that have `piiSafe` set to true. For more info see the [handling PII data](#handling-pii-data) section.\n  - type: boolean\n  - required: no\n  - default value: false\n\n### Alternative logging functions\n\nIf you would prefer to pass in an object to logging functions, instead of using a long list of parameters, you can use the new logging functions available under the `cleLogger.v2` namespace. These functions are namespaced to keep backwards compatibility, but you should feel free to use them going forward. The behavior of these functions is exactly the same as with those listed in [logging statements](#logging-statements) section.\n\nExample verbose logging statement with both versions of the logging function:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(\n\t\"Log message\", // message\n\t{ data: \"test\" }, // additionalData\n\t{ [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" }, // customProperties\n\tcleLogger.componentNames.SESSION, // componentName\n\t70123, // logCode\n\t\"my-token\", // correlationToken\n\ttrue, // containsPII\n);\n\ncleLogger.v2.verbose(\n\t\"Log message\",\n\t{\n\t\tadditionalData: { data: \"test\" },\n\t\tcustomProperties: { [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" },\n\t\tcomponentName: cleLogger.componentNames.SESSION,\n\t\tlogCode: 70123,\n\t\tcorrelationToken: \"my-token\",\n\t\tcontainsPII: true,\n\t}\n);\n```\n\nAs you can see in the examples above, both functions take in the same values, but the `v2` version makes it a bit easier to see which value corresponds to each key. It comes down to personal preference, use whichever you prefer.\n\n### Logging constants\n\n**Logging levels**\n\nLogging level constants should be used when configuring the log level of a [log sink](#log-sinks).\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.logLevels.VERBOSE; // verbose\ncleLogger.logLevels.INFO; // info\ncleLogger.logLevels.WARN; // warn\ncleLogger.logLevels.ERROR; // error\ncleLogger.logLevels.CRITICAL; // critical\n```\n\n**Component names**\n\nDefines a set of commonly used components that should be used for the `componentName` parameter value of a log statement if you are logging from one of these common components in a Twilio implementation.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.componentNames.SESSION;\ncleLogger.componentNames.DIALER;\ncleLogger.componentNames.DASHBOARD;\ncleLogger.componentNames.AFTERCALL;\ncleLogger.componentNames.SUPERVISOR;\n```\n\n**Custom property keys**\n\nDefines a set of commonly used key that should be used when defining the custom properties object that is supplied in the `customProperties` parameter of any logging statement.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.customPropertyKeys.CHANNEL_SID;\ncleLogger.customPropertyKeys.TASK_SID;\ncleLogger.customPropertyKeys.CALL_SID;\ncleLogger.customPropertyKeys.WORKFLOW_SID;\ncleLogger.customPropertyKeys.WORKER_SID;\n```\n\n### Log codes\n\nIt can be helpful when investigating issues to only view logs related to specific areas of a system. Use Log Codes to give a category definition for what area of the contact center the log activity occurred within. Use numbers within the following pre-defined log code ranges or define custom log codes by using your own numbers higher than 300,000.\n\n- 100 000 - 109 999 => CALL_STATUS\n- 110 000 - 119 999 => IVR\n- 120 000 - 129 999 => ROUTING\n- 130 000 - 139 999 => AGENT_ACTION\n- 140 000 - 149 999 => TRANSFER\n- 150 000 - 159 999 => RM_INTEGRATION\n- 160 000 - 169 999 => WFM_INTEGRATION\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\n\n### Registering Twilio Actions\n\nIt is important to log information from the Twilio Flex UI for all implementations even when there is little customization happening. Many developers find it useful to generate log activity when some, or any, of the Twilio Actions occur within the Twilio Flex UI. Twilio Actions are events that arise from the Twilio Flex UI base implementation and indicate when certain activity is occurring - such as an agent hanging up a call. The CLE Logger library can be setup to automatically register all, or selected, Twilio Actions such that right after any code tied to the Action event has been executed a log statement is written indicating the Action occurred. This log statement will also automatically include the action event payload. To enable this logging capability, use the following approach to register the Twilio Actions. This only needs to be performed once during the initialization of the Twilio Flex UI page on the client.\n\nExample in a Twilio Flex UI plugin:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n/**\n * @param flex { typeof import('@twilio/flex-ui') }\n * @param manager { import('@twilio/flex-ui').Manager }\n */\ninit(flex, manager) {\n\tcleLogger.init({\n\t\taccountSid: \"your-account-sid\"\n\t});\n\n\tcleLogger.registerTwilioActions(flex);\n}\n```\n\n**Note**: A Twilio account sid must be provided during `init` with the `accountSid` parameter, otherwise the actions won't be logged.\n\nThe logger will loop through all actions found under `flex.Actions.actions`, and attach `after` event handlers to log the action name and payload. Any private properties found in the payload (starting with '\\_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Not all Action events need to be logged and by using the `excludedFlexActionNames` property in the logger configuration the Action events not desired can be ignored. Any actions names configured in `excludedFlexActionNames` will not be included in the logs.\n\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\n\n**Twilio Actions log levels**\n\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of `INFO`.\n\n```js\nAcceptTask: INFO;\nCancelTransfer: INFO;\nCompleteTask: INFO;\nHangupCall: INFO;\nHideDirectory: VERBOSE;\nHistoryGo: VERBOSE;\nHistoryGoBack: VERBOSE;\nHistoryGoForward: VERBOSE;\nHistoryPush: VERBOSE;\nHistoryReplace: VERBOSE;\nHoldCall: INFO;\nHoldParticipant: INFO;\nKickParticipant: INFO;\nLogout: INFO;\nMonitorCall: INFO;\nNavigateToView: INFO;\nRejectTask: INFO;\nSelectTask: INFO;\nSelectTaskInSupervisor: INFO;\nSelectWorkerInSupervisor: INFO;\nSendMessage: INFO;\nSendTyping: VERBOSE;\nSetActivity: INFO;\nSetInputText: VERBOSE;\nShowDirectory: VERBOSE;\nStopMonitoringCall: INFO;\nToggleMute: INFO;\nToggleSidebar: VERBOSE;\nTransferTask: INFO;\nUnholdCall: INFO;\nUnholdParticipant: INFO;\nWrapupTask: INFO;\n```\n\n### Twilio Event Hooks\n\nCurrently, the Twilio console only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with `twilioEventHooks` option, and use the following functions to send event data to a custom endpoint.\n\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is `application/x-www-form-urlencoded` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// unrelated init properties omitted from this example.\n\t// include in the twilioEventHooks array an entry for only the event hooks\n\t// the executing code is going to relay. For example, if the code is only\n\t// handling task router web hook events then you would only need to define\n\t// the taskRouter relay url here.\n\ttwilioEventHooks: {\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\n\t},\n});\n\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\n```\n\nExample Azure Function hook for debugger events:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\nmodule.exports = async function(context, req) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\t},\n\t});\n\n\t// log to console\n\tcleLogger.info(\"Received Twilio debugger event\");\n\n\t// send event data to a configured url\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\n};\n```\n\n**NOTE WHEN USING THIS FUNCTIONALITY IN TWILIO FUNCTIONS:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to `x-www-form-urlencoded` format.\n\nExample Twilio function task router relay using the `form-urlencoded` npm package to convert the data to `x-www-form-urlencoded` format:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\nconst formurlencoded = require(\"form-urlencoded\").default;\n\nexports.handler = async function(context, event, callback) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\t},\n\t});\n\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\n\n\tcallback();\n};\n```\n\n### Version\n\nVersion will be automatically included in all logs.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.version; // 1.0.0\n```\n\n### Error To Object\n\nUtility function to convert an `Error` object to a plain JS object, with all properties preserved. When serialized, `Error` objects will omit custom properties from the output which makes debugging difficult. This utility will traverse all properties in the `Error` object, and output a plain JS object with all properties including the stack trace. See an example below.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\nconst err = new Error(\"error message\");\nconsole.log(err); // \"Error: error message\\n    at Object.<anonymous> …\"\n\nconst errObject = cleLogger.errorToObject(err);\nconsole.log(errObjct); // {name: 'Error', message: 'error message', stack: 'Error: error message\\n    at Object.<anonymous> …'}\n```\n\nExample usage when logging errors:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ntry {\n\t// some code that can throw an error\n} catch (error) {\n\tcleLogger.error(\"My error message\", cleLogger.errorToObject(error));\n}\n```\n\n## Log Object\n\nThe following is the type definition of the log object received by [log sinks](#log-sinks). It consists of properties provided in log messages, as well as some that are automatically inserted like the `eventDate`. You can use this information to customize your logs inside a custom log sink (e.g. filter out unwanted logs, modify some of the properties to include more data, or any kind of conditional logic based on the value of these properties). If using the built in `remoteLogger` log processor with your custom backend, you can expect an array of these objects to be sent to your log endpoint. For more info about implementing a custom logging endpoint see the [remote logging](#remote-logging) section.\n\n```js\n{\n\t// Account sid if provided during init\n\taccountSid: string;\n\n\t// Any additional log data to be included with the log\n\tadditionalData: unknown;\n\n\t// Component name, if provided\n\tcomponentName: string;\n\n\t// Generated or configured token\n\tcorrelationToken: string;\n\n\t// Current UTC datetime in ISO 8601 format\n\teventDate: string;\n\n\t// Log level\n\tlevel: string;\n\n\t// Log code, if provided\n\tlogCode: number;\n\n\t// Log message\n\tmessage: string;\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\tproperties: object;\n\n\t// BrowserFunction if the environment is the browser\n\t// CloudFunction if the environment is Node.js\n\tsource: string;\n\n\t// Indicates that the log contains PII data\n\tcontainsPII: boolean;\n\n\t// Logging library version\n\tloggerVersion: string;\n}\n```\n\n## Handling PII Data\n\nThe logger provides support for helping to protect log messages or data that need to include PII (Personally Identifiable Information). While the logger has several mechanisms that can be used to flag logs that contain PII and block PII from going to particular log sinks, it is still ultimately the developers responsibility to ensure PII is flagged or marked appropriately for the logger to know when it exists - the logger cannot automatically detect if PII is part of a log statement or data object. The general guideline is that developers should avoid including any PII data in log information whenever possible. In the instances where PII data must be included there are several mechanisms the logger supports to help indicate PII data and protect where it is sent.\n\nA `containsPII` parameter exists on log statement methods. This flag should be set to true if there is PII data, or any potential chance of it, in the log message, additional data or custom properties data. When this flag is set to true then it will only allow log sinks that have the `piiSafe` configuration set to true to receive and process the log message. `piiSafe` indicates on a log sink whether that log sink receiver is able to internally store PII data OR has logic to strip PII data that is tagged in the message or from pre-configured additional data fields before the log is written to any storage or output device. When sending data to any `remoteLogger` endpoint the log sink can be marked `piiSafe` only as long as data is appropriately tagged in messages and/or the names of custom properties that might have PII values in additional data are pre-configured AND the endpoint implementer has verified and ensured their receiving code will remove marked PII data immediately before sending logs to any output or storage media.\n\nTo tag any part of the message parameter in a log statement as containing PII within the actual log message use the `tagPII` utility. The `tagPII` utility will indicate to a remote backend that the text between the tags should be stripped or masked before sending logs to any output or storage media.\n\nExample tagged output:\n\n```js\nimport { tagPII } from \"@prftesp/cle-logger\";\n\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\n```\n\n## Advanced Configuration\n\n### Remote logging\n\nThe library provides a built in way to transmit logs to a remote http endpoint using the `remoteLogger` logs processor. The `remoteLogger` can be directed at a known listener such as CLE or you can create your own custom log listener. In addition, the log level in effect can be controlled remotely setting the `minLogLevelOverrideUrl` to an http address that will return the current log level to use instead of any defined default logLevel on each `logSink`. The `minLogLevelOverrideUrl` can point to a known central API such as CLE or you can create your own endpoint.\n\nThe following sections outline how to create your own endpoints for either receiving log messages from the `remoteLogger` log sink or providing the current log level override to it.\n\n**Remote log endpoint**\n\nTo implement your own remote logging endpoint, all you have to do is accept `POST` requests, with a JSON payload of an array of [log objects](#log-object), and return either `{ success: true }` or `{ success: false }`. The endpoint should always return a `200 OK` status even if your server code fails so that the CLE logger does not cause failures in the business logic in which it is embedded. A good use-case would be leveraging a serverless api platform to output logs to a queue.\n\nExample of an Azure Function that can receive requests from the `remoteLogger` log sink:\n\n```js\nmodule.exports = async function(context, req) {\n\tcontext.log(req.body); // body will have the log array\n\n\tcontext.res = {\n\t\tstatus: 200,\n\t\tbody: { success: true },\n\t};\n};\n```\n\n**Remote log level override**\n\nThe `minLogLevelOverrideUrl` configuration property is used to provide an endpoint that accepts `GET` requests and a `accountSid` query string parameter. `accountSid` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the `minLogLevel` configuration property. If null is returned, the `minLogLevel` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a `200 OK` status code and use the `success` property to indicate failure.\n\nExample success response:\n\n```json\n{\n\t\"success\": true,\n\t\"minLogLevel\": \"warn\"\n}\n```\n\nExample error response:\n\n```json\n{\n\t\"success\": false,\n\t\"minLogLevel\": null\n}\n```\n\n## Changelog\n\n### v2.3.0\n\n**New features**\n\n- To address long-running log requests caused by occasional slow remote log responses, this version introduces default timeouts for log requests. Timeout for Twilio event hooks is 2000 ms and is not configurable. Timeout for the `remoteLogger` helper is 500 ms and can be adjusted with an optional second parameter: `remoteLogger(\"https://mylogger.url\", 1000);`\n\n### v2.2.0\n\n**New features**\n\n- Added [alternative logging functions](#alternative-logging-functions) that have a much shorter parameter list, and allow you to specify only those logging properties that you want, without the need to pass in `null` for parameters you are not interested in. New functions are available under `cleLogger.v2` for backward compatibility reasons.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n// if you want to log just the message and the token, \n// you need to default parameters to null until you get to token\ncleLogger.verbose(\"Log message\", null, null, null, null, \"my-token\");\n\n// with the new functions, you just provide an object with the properties you want\ncleLogger.v2.verbose(\"Log message\", { correlationToken: \"my-token\" });\n```\n\n### v2.1.0\n\n**New features**\n\n- Added [Error To Object](#error-to-object) method to assist users in converting error object to plain objects.\n\n### v2.0.7\n\n**New features**\n\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the call sid during a call, and automatically include it in `customProperties` log property for all logs that happen during that call (including Flex Action logs).\n\n### v2.0.5\n\n**New features**\n\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the worker sid and automatically include it in `customProperties` log property.\n\n**Other updates**\n\n- Updated documentation based on user feedback.\n\n### v2.0.4\n\n**Bug fixes**\n\n- Fixed issue with logging to console in Twilio Functions. The `consoleLogger` will now output the log object as serialized json.\n\n### v2.0.0 [2019-12-20]\n\n**New features**\n\n- All logging output is now configured using the `logSinks` property. The library provides two commonly used loggers - `consoleLogger` and a `remoteLogger` that accepts the log url as a parameter. [Configuration](#configuration)\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\n- Log functions have a new optional `containsPII` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\n- A new helper function `tagPII` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\n\n**Breaking changes**\n\n- `customLogSink` configuration renamed to `logSinks`\n- Removed `disableRemoteLogging` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\n- Removed `logToConsole` configuration. This is now configured as a log sink with a `consoleLogger` helper function.\n- Removed `logUrl` configuration. This is now configured as a log sink with a `remoteLogger` helper function.\n- Removed `minLogLevel` configuration. This is now configured per log sink.\n\n**Migration guide**\n\n```js\nimport cleLogger from \"@prftesp/cle-logger\";\n\n// v1.0.0 and older config\ncleLogger.init({\n\tdisableRemoteLogSinks: true, // no direct replacement\n\tlogToConsole: true, // replaced by the \"console logger\" sink\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\n\tcustomLogSinks: [\n\t\t{\n\t\t\tname: \"custom sink\",\n\t\t\tlog: logs => {\n\t\t\t\t/* your custom logging */\n\t\t\t},\n\t\t},\n\t],\n});\n\n// v2.0.0 config\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"custom sink\",\n\t\t\tlog: logs => {\n\t\t\t\t/* your custom logging */\n\t\t\t},\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"console logger\",\n\t\t\tlog: consoleLogger,\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"remote logger\",\n\t\t\tlog: remoteLogger(\"http://log.output/api\"),\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t],\n});\n```\n\n### v1.0.0 [2019-09-12]\n\n- Initial release\n","readmeFilename":"README.md","_nodeVersion":"18.19.0","_npmVersion":"10.2.3","dist":{"integrity":"sha512-CTWHuNqRuUYGvTAZjWCG2yPglAn8opwGQLReSCAJ3sflMdGLaeEoKIKMD0jrO54wsGtjFzpPopbVEreJ3im6Sg==","shasum":"b068128bfca248072d4e4da5f221034a8e10e6b4","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.3.1-beta.20240131.2.tgz","fileCount":17,"unpackedSize":90112,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDOqlz6Tpwp8i5pk/nLAcvc0ZwzdUgWpvIqMdP9JlRDXgIhAJOUpx8k3OsvtkqIOoxtMTDlA4PyTkzTi5yz2BsgeqAR"}]},"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"maintainers":[{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"},{"name":"shelby-prft","email":"shelby.hagman@perficient.com"},{"name":"curtis.swartzentruber","email":"c.swartzentruber@perficient.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.3.1-beta.20240131.2_1706719448371_0.8705170878964419"},"_hasShrinkwrap":false},"2.3.1-beta.20240131.3":{"name":"@prftesp/cle-logger","version":"2.3.1-beta.20240131.3","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* -g && tsc --build && npm run build:es5","build:es5":"rimraf dist-es5/* -g && tsc --build tsconfig.es5.json","test":"jest --runInBand --silent --config jest.config.js","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^29.4.0","@types/node":"^18.14.6","@typescript-eslint/eslint-plugin":"^5.54.1","@typescript-eslint/parser":"^5.54.1","eslint":"^8.35.0","eslint-config-prettier":"^8.7.0","jest":"^29.5.0","prettier":"^2.8.4","rimraf":"^4.3.1","ts-jest":"^29.0.5","typescript":"^4.9.5"},"dependencies":{"axios":"^1.3.4","serializerr":"^1.0.3"},"_id":"@prftesp/cle-logger@2.3.1-beta.20240131.3","readme":"﻿CLE Logger\n==\n\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\n\n## Documentation\n\n- [Target Environment](#target-environment)\n- [Simple Example](#simple-example)\n- [Installation](#installation)\n- [Features](#features)\n- [Configuration](#configuration)\n- [API](#api)\n  - [Log sinks](#log-sinks)\n  - [Logging statements](#logging-statements)\n  - [Logging parameters](#logging-parameters)\n  - [Alternative logging functions](#alternative-logging-functions)\n  - [Logging constants](#logging-constants)\n  - [Log codes](#log-codes)\n  - [Registering Twilio Actions](#registering-twilio-actions)\n  - [Twilio Event Hooks](#twilio-event-hooks)\n  - [Version](#version)\n  - [Error To Object](#error-to-object)\n- [Log Object](#log-object)\n- [Handling PII Data](#handling-pii-data)\n- [Advanced Configuration](#advanced-configuration)\n  - [Remote logging](#remote-logging)\n- [Changelog](#changelog)\n\n## Target Environment\n\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\n\n### ES5 Compatible Version\n\nIf you need an ES5 compatible version (for example you need IE11 support), use this package: https://www.npmjs.com/package/@prftesp/cle-logger-es5. **This package is not regularly released with the main library, and should be used only when absolutely necessary**. If you need to support IE11, don't forget to include the necessary polyfills, mainly for `Promise` support. To do this, you can include this script tag on the page, before you load this library `<script src=\"https://cdn.polyfill.io/v3/polyfill.min.js?features=es6\"></script>`, or visit `https://polyfill.io` for more options.\n\n**Please note** that the ES5 version is generated, and is sharing the same code and documentation as the main library. This is why the rest of the documentation doesn't mention the ES5 version in text or examples. The only difference is how you reference it (`@prftesp/cle-logger` vs `@prftesp/cle-logger-es5`). To install it use `npm install @prftesp/cle-logger-es5`, and to import it use `import { cleLogger } from \"@prftesp/cle-logger-es5\";`.\n\n## Simple Example\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"console sink\",\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\tpiiSafe: true,\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n});\n\n// will log message to console\ncleLogger.info(\"log message\");\n```\n\n## Installation\n\n```bash\n$ npm install @prftesp/cle-logger\n```\n\n## Features\n\n- Runs in Node.js and the browser\n- Remote logging\n- Multiple log sinks\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\n- Remote minimum log level overrides\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\n- Support for tagging logs with custom component names or correlation tokens\n- Send Twilio event hook data to a custom backend\n\n## Configuration\n\nTo configure the logger call the `init` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\n\n**Available options**\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// Twilio account sid, required for use with CLE\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\n\n\t// Will be associated with all logs,\n\t// if not provided, a unique uuid will be generated\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\n\n\t// An array of that will receive an array of log objects,\n\t// and can optionally return a promise. Please see the\n\t// \"Log Sinks\" section for more information.\n\tlogSinks: [\n\t\t{\n\t\t\t// name of the log sink\n\t\t\tname: \"exampleConsoleSink\",\n\t\t\t// log handler function\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\t// This flag defines if the log message is processed by this sink when the contains\n\t\t\t// PII parameter of the log statement is true. piiSafe of true indicates all log\n\t\t\t// messages are allowed (even when contains PII is true); piiSafe of false indicates\n\t\t\t// only log messages with contains PII set to false will be processed by this log sink.\n\t\t\t// Defaults to false\n\t\t\tpiiSafe: true,\n\t\t\t// log level for the sink, defaults to INFO\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n\n\t// A way to display additional errors internal to the logger, useful for debugging, defaults to false\n\tlogErrorsToConsole: false,\n\n\t// If logging Twilio Flex Actions, provide a list of actions\n\t// to be excluded from logs\n\texcludedFlexActionNames: [\"SendTyping\"],\n\n\t// A custom endpoint to fetch log level overrides\n\t// NOTE: the provided url should always end with 'accountSid=',\n\t// the library will append the accountSid\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\n\n\t// An instance of ('@twilio/flex-ui').Manager.\n\t// If provided, it will be used to get the worker sid and call sid (only during a call), and\n\t// automatically include it in 'customProperties' log property and with every log\n\ttwilioFlexManager: manager,\n\n\t// To send data from Twilio event hooks, to a custom backend,\n\t// configure one or more endpoints for each event type\n\ttwilioEventHooks: {\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\n\t},\n});\n```\n\n## API\n\nThe logger can be imported as an object in CommonJs or ES6 syntax:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n// or\nconst cleLogger = require(\"@prftesp/cle-logger\");\n```\n\nAlternatively, all top level members of the `cleLogger` are available as named exports and can be imported separately:\n\n```js\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\n\ninit({\n\tlogSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: logLevels.VERBOSE\n\t}],\n});\n\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\n\n// vs\n\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ninit({\n\tcleLogger.logSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: cleLogger.consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}],\n});\n\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\n```\n\n### Log sinks\n\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the `logSinks` config. The logSinks configuration defines an array of objects with each object defining the log sink activity. All provided log sinks will be executed in parallel. A log sink configuration must include the following properties:\n\n- `name`: this is an identifiable name for the logger and must be unique per log sink defined\n- `log`: this indicates the logic to process on each batch of logs processed by the sink. There are several built in processing functions that can be used or you can insert any code inline with this definition. The built in processing functions are `consoleLogger` which will send the log statements to the attached console or `remoteLogger` which will send the log statements to the specified url provided as a definition on the parameter (i.e. `remoteLogger(\"https://mylogger.url\"`). For a custom log processing implementation, the logic must be async operations and must return a Promise object. The custom log method will receive an array of log objects for you to handle. You can find out more about the log object type in the [Log Object](#log-object) section. Here is an example of a custom log function:\n\n```js\nlogs => {\n\t// log each received log to the console separately\n\tlogs.forEach(log => console.log(log));\n\treturn Promise.resolve();\n};\n```\n\n- `logLevel`: defines the minimum default log level that will be processed by this log sink. Log levels lower than the minimum level will not be processed by this log sink. The minimum default log level can be overridden at run time by the value returned from the overall `minLogLevelOverrideUrl` that is set up in the library `init`.\n- `piiSafe`: indicates if log messages with a `containsPII` parameter value of true should be processed by the log sink. If `containsPII` parameter value on the log message is true and piiSafe is false then the log message will not be processed by this log sink, if piiSafe is true then it will. If the `containsPII` parameter value is false on the log message than it is always processed by the log sink regardless of the piiSafe value.\n\nCustom log sink configuration example. In this example, the custom code executed for each log statement is to append the log into a file. Note how a Promise is returned as required:\n\n```js\n{\n\t// name of the log sink\n\tname: \"exampleConsoleSink\",\n\t// log handler function - this is where you put your custom handling code and\n\t// remember to always return a promise. If your operation is not asynchronous,\n\t// you can do this by returning Promise.resolve().\n\tlog: logs => {\n\t\tfs.appendFile(\n\t\t\t\"./log.txt\",\n\t\t\t`${JSON.stringify(logs)}`,\n\t\t\tconsole.log\n\t\t);\n\t\treturn Promise.resolve();\n\t},\n\t// should this log sink accept PII data, defaults to false\n\tpiiSafe: true,\n\t// log level for the sink, defaults to INFO\n\tlogLevel: cleLogger.logLevels.VERBOSE\n}\n```\n\nExample log sink configuration that uses a custom coded sink and both standard console and remote logging sinks:\n\n```js\nconst fs = require(\"fs\");\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"fileSink\",\n\t\t\tlog: logs => {\n\t\t\t\tfs.appendFile(\"./log.txt\", `${JSON.stringify(logs)}`, console.log);\n\t\t\t\treturn Promise.resolve();\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"remoteSink\",\n\t\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\n\t\t\tlogLevel: cleLogger.logLevels.WARN,\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"consoleSink\",\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n});\n```\n\n### Logging statements\n\nTo create logs, you can use one of the logging function available. They all take the same arguments, described below, and log at a corresponding log level. For example, if you use the `cleLogger.verbose` logging function, and your log sink is configured with `logLevel: cleLogger.logLevels.VERBOSE`, that log sink will receive the log. If your log sink is configured with a higher log level, for example `logLevel: cleLogger.logLevels.INFO`, then it would not receive the verbose log.\n\nHere are the available logging functions:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(...);\ncleLogger.info(...);\ncleLogger.warn(...);\ncleLogger.error(...);\ncleLogger.critical(...);\n```\n\nThe functions are all async, return `Promise<void>`, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\n\n### Logging parameters\n\nAll logging functions take the same parameters:\n\n- `message`: the only required property, no logs will be generated without it\n  - type: string\n  - required: yes\n- `additionalData`: any additional log data you would like to include with the log, can be any data type\n  - type: any\n  - required: no\n  - default value: null\n- `customProperties`: a single level deep object of important properties you would like to distinguish from general log data, like various sids CLE uses it to index these properties in Application Insights. For CLE, try to use keys found in `cleLogger.customPropertyKeys`\n  - type: object\n  - required: no\n  - default value: {}\n- `componentName`: another useful way to 'tag' your logs for easier searching. Can be any string, but for CLE, try to use available options in `cleLogger.componentNames`\n  - type: string\n  - required: no\n  - default value: \"\"\n- `logCode`: log code, can be any number used to categorize logs. For defined ranges see the [log codes](#log-codes) section.\n  - type: number\n  - required: no\n  - default value: 0\n- `correlationToken`: correlation token to be associated with this log only\n  - type: string\n  - required: no\n  - default value: token provided in `init`, or a generated token if one wasn't provided\n- `containsPII`: does the log contain PII data, defaults to false. If set to true, the log will be passed in only to those log sinks that have `piiSafe` set to true. For more info see the [handling PII data](#handling-pii-data) section.\n  - type: boolean\n  - required: no\n  - default value: false\n\n### Alternative logging functions\n\nIf you would prefer to pass in an object to logging functions, instead of using a long list of parameters, you can use the new logging functions available under the `cleLogger.v2` namespace. These functions are namespaced to keep backwards compatibility, but you should feel free to use them going forward. The behavior of these functions is exactly the same as with those listed in [logging statements](#logging-statements) section.\n\nExample verbose logging statement with both versions of the logging function:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(\n\t\"Log message\", // message\n\t{ data: \"test\" }, // additionalData\n\t{ [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" }, // customProperties\n\tcleLogger.componentNames.SESSION, // componentName\n\t70123, // logCode\n\t\"my-token\", // correlationToken\n\ttrue, // containsPII\n);\n\ncleLogger.v2.verbose(\n\t\"Log message\",\n\t{\n\t\tadditionalData: { data: \"test\" },\n\t\tcustomProperties: { [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" },\n\t\tcomponentName: cleLogger.componentNames.SESSION,\n\t\tlogCode: 70123,\n\t\tcorrelationToken: \"my-token\",\n\t\tcontainsPII: true,\n\t}\n);\n```\n\nAs you can see in the examples above, both functions take in the same values, but the `v2` version makes it a bit easier to see which value corresponds to each key. It comes down to personal preference, use whichever you prefer.\n\n### Logging constants\n\n**Logging levels**\n\nLogging level constants should be used when configuring the log level of a [log sink](#log-sinks).\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.logLevels.VERBOSE; // verbose\ncleLogger.logLevels.INFO; // info\ncleLogger.logLevels.WARN; // warn\ncleLogger.logLevels.ERROR; // error\ncleLogger.logLevels.CRITICAL; // critical\n```\n\n**Component names**\n\nDefines a set of commonly used components that should be used for the `componentName` parameter value of a log statement if you are logging from one of these common components in a Twilio implementation.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.componentNames.SESSION;\ncleLogger.componentNames.DIALER;\ncleLogger.componentNames.DASHBOARD;\ncleLogger.componentNames.AFTERCALL;\ncleLogger.componentNames.SUPERVISOR;\n```\n\n**Custom property keys**\n\nDefines a set of commonly used key that should be used when defining the custom properties object that is supplied in the `customProperties` parameter of any logging statement.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.customPropertyKeys.CHANNEL_SID;\ncleLogger.customPropertyKeys.TASK_SID;\ncleLogger.customPropertyKeys.CALL_SID;\ncleLogger.customPropertyKeys.WORKFLOW_SID;\ncleLogger.customPropertyKeys.WORKER_SID;\n```\n\n### Log codes\n\nIt can be helpful when investigating issues to only view logs related to specific areas of a system. Use Log Codes to give a category definition for what area of the contact center the log activity occurred within. Use numbers within the following pre-defined log code ranges or define custom log codes by using your own numbers higher than 300,000.\n\n- 100 000 - 109 999 => CALL_STATUS\n- 110 000 - 119 999 => IVR\n- 120 000 - 129 999 => ROUTING\n- 130 000 - 139 999 => AGENT_ACTION\n- 140 000 - 149 999 => TRANSFER\n- 150 000 - 159 999 => RM_INTEGRATION\n- 160 000 - 169 999 => WFM_INTEGRATION\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\n\n### Registering Twilio Actions\n\nIt is important to log information from the Twilio Flex UI for all implementations even when there is little customization happening. Many developers find it useful to generate log activity when some, or any, of the Twilio Actions occur within the Twilio Flex UI. Twilio Actions are events that arise from the Twilio Flex UI base implementation and indicate when certain activity is occurring - such as an agent hanging up a call. The CLE Logger library can be setup to automatically register all, or selected, Twilio Actions such that right after any code tied to the Action event has been executed a log statement is written indicating the Action occurred. This log statement will also automatically include the action event payload. To enable this logging capability, use the following approach to register the Twilio Actions. This only needs to be performed once during the initialization of the Twilio Flex UI page on the client.\n\nExample in a Twilio Flex UI plugin:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n/**\n * @param flex { typeof import('@twilio/flex-ui') }\n * @param manager { import('@twilio/flex-ui').Manager }\n */\ninit(flex, manager) {\n\tcleLogger.init({\n\t\taccountSid: \"your-account-sid\"\n\t});\n\n\tcleLogger.registerTwilioActions(flex);\n}\n```\n\n**Note**: A Twilio account sid must be provided during `init` with the `accountSid` parameter, otherwise the actions won't be logged.\n\nThe logger will loop through all actions found under `flex.Actions.actions`, and attach `after` event handlers to log the action name and payload. Any private properties found in the payload (starting with '\\_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Not all Action events need to be logged and by using the `excludedFlexActionNames` property in the logger configuration the Action events not desired can be ignored. Any actions names configured in `excludedFlexActionNames` will not be included in the logs.\n\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\n\n**Twilio Actions log levels**\n\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of `INFO`.\n\n```js\nAcceptTask: INFO;\nCancelTransfer: INFO;\nCompleteTask: INFO;\nHangupCall: INFO;\nHideDirectory: VERBOSE;\nHistoryGo: VERBOSE;\nHistoryGoBack: VERBOSE;\nHistoryGoForward: VERBOSE;\nHistoryPush: VERBOSE;\nHistoryReplace: VERBOSE;\nHoldCall: INFO;\nHoldParticipant: INFO;\nKickParticipant: INFO;\nLogout: INFO;\nMonitorCall: INFO;\nNavigateToView: INFO;\nRejectTask: INFO;\nSelectTask: INFO;\nSelectTaskInSupervisor: INFO;\nSelectWorkerInSupervisor: INFO;\nSendMessage: INFO;\nSendTyping: VERBOSE;\nSetActivity: INFO;\nSetInputText: VERBOSE;\nShowDirectory: VERBOSE;\nStopMonitoringCall: INFO;\nToggleMute: INFO;\nToggleSidebar: VERBOSE;\nTransferTask: INFO;\nUnholdCall: INFO;\nUnholdParticipant: INFO;\nWrapupTask: INFO;\n```\n\n### Twilio Event Hooks\n\nCurrently, the Twilio console only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with `twilioEventHooks` option, and use the following functions to send event data to a custom endpoint.\n\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is `application/x-www-form-urlencoded` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// unrelated init properties omitted from this example.\n\t// include in the twilioEventHooks array an entry for only the event hooks\n\t// the executing code is going to relay. For example, if the code is only\n\t// handling task router web hook events then you would only need to define\n\t// the taskRouter relay url here.\n\ttwilioEventHooks: {\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\n\t},\n});\n\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\n```\n\nExample Azure Function hook for debugger events:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\nmodule.exports = async function(context, req) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\t},\n\t});\n\n\t// log to console\n\tcleLogger.info(\"Received Twilio debugger event\");\n\n\t// send event data to a configured url\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\n};\n```\n\n**NOTE WHEN USING THIS FUNCTIONALITY IN TWILIO FUNCTIONS:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to `x-www-form-urlencoded` format.\n\nExample Twilio function task router relay using the `form-urlencoded` npm package to convert the data to `x-www-form-urlencoded` format:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\nconst formurlencoded = require(\"form-urlencoded\").default;\n\nexports.handler = async function(context, event, callback) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\t},\n\t});\n\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\n\n\tcallback();\n};\n```\n\n### Version\n\nVersion will be automatically included in all logs.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.version; // 1.0.0\n```\n\n### Error To Object\n\nUtility function to convert an `Error` object to a plain JS object, with all properties preserved. When serialized, `Error` objects will omit custom properties from the output which makes debugging difficult. This utility will traverse all properties in the `Error` object, and output a plain JS object with all properties including the stack trace. See an example below.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\nconst err = new Error(\"error message\");\nconsole.log(err); // \"Error: error message\\n    at Object.<anonymous> …\"\n\nconst errObject = cleLogger.errorToObject(err);\nconsole.log(errObjct); // {name: 'Error', message: 'error message', stack: 'Error: error message\\n    at Object.<anonymous> …'}\n```\n\nExample usage when logging errors:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ntry {\n\t// some code that can throw an error\n} catch (error) {\n\tcleLogger.error(\"My error message\", cleLogger.errorToObject(error));\n}\n```\n\n## Log Object\n\nThe following is the type definition of the log object received by [log sinks](#log-sinks). It consists of properties provided in log messages, as well as some that are automatically inserted like the `eventDate`. You can use this information to customize your logs inside a custom log sink (e.g. filter out unwanted logs, modify some of the properties to include more data, or any kind of conditional logic based on the value of these properties). If using the built in `remoteLogger` log processor with your custom backend, you can expect an array of these objects to be sent to your log endpoint. For more info about implementing a custom logging endpoint see the [remote logging](#remote-logging) section.\n\n```js\n{\n\t// Account sid if provided during init\n\taccountSid: string;\n\n\t// Any additional log data to be included with the log\n\tadditionalData: unknown;\n\n\t// Component name, if provided\n\tcomponentName: string;\n\n\t// Generated or configured token\n\tcorrelationToken: string;\n\n\t// Current UTC datetime in ISO 8601 format\n\teventDate: string;\n\n\t// Log level\n\tlevel: string;\n\n\t// Log code, if provided\n\tlogCode: number;\n\n\t// Log message\n\tmessage: string;\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\tproperties: object;\n\n\t// BrowserFunction if the environment is the browser\n\t// CloudFunction if the environment is Node.js\n\tsource: string;\n\n\t// Indicates that the log contains PII data\n\tcontainsPII: boolean;\n\n\t// Logging library version\n\tloggerVersion: string;\n}\n```\n\n## Handling PII Data\n\nThe logger provides support for helping to protect log messages or data that need to include PII (Personally Identifiable Information). While the logger has several mechanisms that can be used to flag logs that contain PII and block PII from going to particular log sinks, it is still ultimately the developers responsibility to ensure PII is flagged or marked appropriately for the logger to know when it exists - the logger cannot automatically detect if PII is part of a log statement or data object. The general guideline is that developers should avoid including any PII data in log information whenever possible. In the instances where PII data must be included there are several mechanisms the logger supports to help indicate PII data and protect where it is sent.\n\nA `containsPII` parameter exists on log statement methods. This flag should be set to true if there is PII data, or any potential chance of it, in the log message, additional data or custom properties data. When this flag is set to true then it will only allow log sinks that have the `piiSafe` configuration set to true to receive and process the log message. `piiSafe` indicates on a log sink whether that log sink receiver is able to internally store PII data OR has logic to strip PII data that is tagged in the message or from pre-configured additional data fields before the log is written to any storage or output device. When sending data to any `remoteLogger` endpoint the log sink can be marked `piiSafe` only as long as data is appropriately tagged in messages and/or the names of custom properties that might have PII values in additional data are pre-configured AND the endpoint implementer has verified and ensured their receiving code will remove marked PII data immediately before sending logs to any output or storage media.\n\nTo tag any part of the message parameter in a log statement as containing PII within the actual log message use the `tagPII` utility. The `tagPII` utility will indicate to a remote backend that the text between the tags should be stripped or masked before sending logs to any output or storage media.\n\nExample tagged output:\n\n```js\nimport { tagPII } from \"@prftesp/cle-logger\";\n\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\n```\n\n## Advanced Configuration\n\n### Remote logging\n\nThe library provides a built in way to transmit logs to a remote http endpoint using the `remoteLogger` logs processor. The `remoteLogger` can be directed at a known listener such as CLE or you can create your own custom log listener. In addition, the log level in effect can be controlled remotely setting the `minLogLevelOverrideUrl` to an http address that will return the current log level to use instead of any defined default logLevel on each `logSink`. The `minLogLevelOverrideUrl` can point to a known central API such as CLE or you can create your own endpoint.\n\nThe following sections outline how to create your own endpoints for either receiving log messages from the `remoteLogger` log sink or providing the current log level override to it.\n\n**Remote log endpoint**\n\nTo implement your own remote logging endpoint, all you have to do is accept `POST` requests, with a JSON payload of an array of [log objects](#log-object), and return either `{ success: true }` or `{ success: false }`. The endpoint should always return a `200 OK` status even if your server code fails so that the CLE logger does not cause failures in the business logic in which it is embedded. A good use-case would be leveraging a serverless api platform to output logs to a queue.\n\nExample of an Azure Function that can receive requests from the `remoteLogger` log sink:\n\n```js\nmodule.exports = async function(context, req) {\n\tcontext.log(req.body); // body will have the log array\n\n\tcontext.res = {\n\t\tstatus: 200,\n\t\tbody: { success: true },\n\t};\n};\n```\n\n**Remote log level override**\n\nThe `minLogLevelOverrideUrl` configuration property is used to provide an endpoint that accepts `GET` requests and a `accountSid` query string parameter. `accountSid` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the `minLogLevel` configuration property. If null is returned, the `minLogLevel` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a `200 OK` status code and use the `success` property to indicate failure.\n\nExample success response:\n\n```json\n{\n\t\"success\": true,\n\t\"minLogLevel\": \"warn\"\n}\n```\n\nExample error response:\n\n```json\n{\n\t\"success\": false,\n\t\"minLogLevel\": null\n}\n```\n\n## Changelog\n\n### v2.3.0\n\n**New features**\n\n- To address long-running log requests caused by occasional slow remote log responses, this version introduces default timeouts for log requests. Timeout for Twilio event hooks is 2000 ms and is not configurable. Timeout for the `remoteLogger` helper is 500 ms and can be adjusted with an optional second parameter: `remoteLogger(\"https://mylogger.url\", 1000);`\n\n### v2.2.0\n\n**New features**\n\n- Added [alternative logging functions](#alternative-logging-functions) that have a much shorter parameter list, and allow you to specify only those logging properties that you want, without the need to pass in `null` for parameters you are not interested in. New functions are available under `cleLogger.v2` for backward compatibility reasons.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n// if you want to log just the message and the token, \n// you need to default parameters to null until you get to token\ncleLogger.verbose(\"Log message\", null, null, null, null, \"my-token\");\n\n// with the new functions, you just provide an object with the properties you want\ncleLogger.v2.verbose(\"Log message\", { correlationToken: \"my-token\" });\n```\n\n### v2.1.0\n\n**New features**\n\n- Added [Error To Object](#error-to-object) method to assist users in converting error object to plain objects.\n\n### v2.0.7\n\n**New features**\n\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the call sid during a call, and automatically include it in `customProperties` log property for all logs that happen during that call (including Flex Action logs).\n\n### v2.0.5\n\n**New features**\n\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the worker sid and automatically include it in `customProperties` log property.\n\n**Other updates**\n\n- Updated documentation based on user feedback.\n\n### v2.0.4\n\n**Bug fixes**\n\n- Fixed issue with logging to console in Twilio Functions. The `consoleLogger` will now output the log object as serialized json.\n\n### v2.0.0 [2019-12-20]\n\n**New features**\n\n- All logging output is now configured using the `logSinks` property. The library provides two commonly used loggers - `consoleLogger` and a `remoteLogger` that accepts the log url as a parameter. [Configuration](#configuration)\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\n- Log functions have a new optional `containsPII` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\n- A new helper function `tagPII` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\n\n**Breaking changes**\n\n- `customLogSink` configuration renamed to `logSinks`\n- Removed `disableRemoteLogging` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\n- Removed `logToConsole` configuration. This is now configured as a log sink with a `consoleLogger` helper function.\n- Removed `logUrl` configuration. This is now configured as a log sink with a `remoteLogger` helper function.\n- Removed `minLogLevel` configuration. This is now configured per log sink.\n\n**Migration guide**\n\n```js\nimport cleLogger from \"@prftesp/cle-logger\";\n\n// v1.0.0 and older config\ncleLogger.init({\n\tdisableRemoteLogSinks: true, // no direct replacement\n\tlogToConsole: true, // replaced by the \"console logger\" sink\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\n\tcustomLogSinks: [\n\t\t{\n\t\t\tname: \"custom sink\",\n\t\t\tlog: logs => {\n\t\t\t\t/* your custom logging */\n\t\t\t},\n\t\t},\n\t],\n});\n\n// v2.0.0 config\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"custom sink\",\n\t\t\tlog: logs => {\n\t\t\t\t/* your custom logging */\n\t\t\t},\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"console logger\",\n\t\t\tlog: consoleLogger,\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"remote logger\",\n\t\t\tlog: remoteLogger(\"http://log.output/api\"),\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t],\n});\n```\n\n### v1.0.0 [2019-09-12]\n\n- Initial release\n","readmeFilename":"README.md","_nodeVersion":"18.19.0","_npmVersion":"10.2.3","dist":{"integrity":"sha512-CQaXEpgV0YS7Qva+HihtxFG0Hj3Bu4Xgqf3qVlWqFdELqujb3dRhLOc5Zx0ml2OqjtbViKr4+RIlENQ48gK6Qg==","shasum":"2ef5305a0a34859107baf926bb359ceceaf98ebe","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.3.1-beta.20240131.3.tgz","fileCount":17,"unpackedSize":90112,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHJkIQE1Z9HQlxKglkN/x/mo4dBPAj1FjDAnetZy6WgCAiEAmrtX0CoquL+hYunRq69iNj0W1aSogmFO8BNiDOGKmnw="}]},"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"maintainers":[{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"},{"name":"shelby-prft","email":"shelby.hagman@perficient.com"},{"name":"curtis.swartzentruber","email":"c.swartzentruber@perficient.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.3.1-beta.20240131.3_1706720673295_0.034969640166364835"},"_hasShrinkwrap":false},"2.3.1":{"name":"@prftesp/cle-logger","version":"2.3.1","description":"CLE Node Logging Library","main":"dist/index.js","types":"dist/index.d.ts","author":{"name":"Perficient, Inc."},"engines":{"node":">=8.10.0"},"keywords":[],"license":"ISC","scripts":{"build":"rimraf dist/* -g && tsc --build && npm run build:es5","build:es5":"rimraf dist-es5/* -g && tsc --build tsconfig.es5.json","test":"jest --runInBand --silent --config jest.config.js","lint":"eslint \"src/**/*.ts\" \"tests/**/*.ts\"","lint-fix":"npm run lint -- --fix","ci-check":"npm run build && npm run lint && npm test","publish-lib":"npm publish --access public","publish-lib-beta":"npm publish --access public --tag beta"},"devDependencies":{"@types/axios":"^0.14.0","@types/jest":"^29.4.0","@types/node":"^18.14.6","@typescript-eslint/eslint-plugin":"^5.54.1","@typescript-eslint/parser":"^5.54.1","eslint":"^8.35.0","eslint-config-prettier":"^8.7.0","jest":"^29.5.0","prettier":"^2.8.4","rimraf":"^4.3.1","ts-jest":"^29.0.5","typescript":"^4.9.5"},"dependencies":{"axios":"^1.3.4","serializerr":"^1.0.3"},"_id":"@prftesp/cle-logger@2.3.1","_nodeVersion":"18.19.0","_npmVersion":"10.2.3","dist":{"integrity":"sha512-HhL3EB9FI81F9mt0nkklRf/+e8QpSirtLD6h+d05VtC2EACMPVabPjr0EAYubfsPXng0vBgzUJMuUKVzI4ak3w==","shasum":"840314a82b61814078beff31dee2bb0162b35193","tarball":"https://registry.npmjs.org/@prftesp/cle-logger/-/cle-logger-2.3.1.tgz","fileCount":17,"unpackedSize":90011,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDNGMfwNHa3Tr1tau++t4sDDVnTb30b5k0ThRMJ9s+IDgIhAM1A8i1qo+Qltz86od/J+Uq8LTaP9eRczGVGKtMvRY+f"}]},"_npmUser":{"name":"espnpm","email":"espnpm@perficient.com"},"directories":{},"maintainers":[{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"},{"name":"shelby-prft","email":"shelby.hagman@perficient.com"},{"name":"curtis.swartzentruber","email":"c.swartzentruber@perficient.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/cle-logger_2.3.1_1706727067894_0.9506867881588426"},"_hasShrinkwrap":false}},"time":{"created":"2019-06-26T22:16:08.720Z","0.0.1":"2019-06-26T22:16:09.136Z","modified":"2024-01-31T18:51:08.353Z","0.0.2":"2019-06-26T22:17:13.133Z","0.0.3":"2019-06-26T22:18:19.557Z","0.0.4":"2019-06-26T22:21:57.205Z","0.0.5":"2019-06-26T22:23:25.974Z","0.0.6":"2019-06-26T22:24:30.227Z","0.0.7":"2019-06-28T15:43:13.608Z","0.0.8":"2019-06-28T15:47:27.305Z","0.0.9":"2019-07-05T13:10:35.706Z","0.0.11-beta.1691":"2019-07-05T16:20:29.662Z","0.0.11-beta.1696":"2019-07-05T16:35:50.462Z","0.0.11":"2019-07-05T16:42:13.417Z","0.0.12-beta.20190705.4":"2019-07-05T18:00:28.589Z","0.0.12":"2019-07-05T18:02:40.658Z","0.0.13-beta.1":"2019-07-08T13:29:20.883Z","0.0.13":"2019-07-09T21:15:54.587Z","0.0.14":"2019-07-09T23:50:56.468Z","0.0.15":"2019-07-10T00:03:15.298Z","0.0.16":"2019-07-10T18:15:17.963Z","0.0.17":"2019-07-11T01:16:36.057Z","0.0.18":"2019-07-11T01:24:59.902Z","0.0.18-beta.1":"2019-07-11T01:26:09.972Z","0.0.18-beta.2":"2019-07-11T01:29:15.481Z","0.0.18-beta.3":"2019-07-11T01:30:02.115Z","0.0.18-beta.4":"2019-07-11T01:39:01.881Z","0.0.18-beta.5":"2019-07-11T01:41:13.061Z","0.0.18-beta.6":"2019-07-11T01:42:28.820Z","0.0.18-beta.7":"2019-07-11T01:43:00.560Z","0.0.18-beta.8":"2019-07-11T01:45:54.666Z","0.0.19":"2019-07-11T01:46:56.679Z","0.0.20":"2019-07-11T01:49:27.363Z","0.0.21":"2019-07-11T15:55:58.102Z","0.0.22-beta.1":"2019-07-11T16:01:48.854Z","0.0.22-beta.2":"2019-07-11T17:34:55.105Z","0.0.22-beta.3":"2019-07-11T18:07:06.340Z","0.0.22-beta.4":"2019-07-11T18:13:42.103Z","0.0.23":"2019-07-11T18:16:11.422Z","0.0.24":"2019-07-11T18:42:37.915Z","0.0.24-beta.20190711.1":"2019-07-11T21:15:29.564Z","0.0.25-beta.20190711.2":"2019-07-12T01:32:09.836Z","0.0.25":"2019-07-12T01:34:27.489Z","0.0.26-beta.20190712.2":"2019-07-12T13:59:52.180Z","0.0.26":"2019-07-12T14:02:19.174Z","0.0.28-beta.20190712.4":"2019-07-12T16:06:29.016Z","0.0.28":"2019-07-12T16:09:32.239Z","0.0.29":"2019-07-16T16:24:51.427Z","0.0.30-beta.20190731.1":"2019-07-31T15:59:55.942Z","0.0.30":"2019-07-31T16:02:58.362Z","0.0.30-beta.20190830.2":"2019-08-30T16:12:18.634Z","1.0.0-beta.1":"2019-09-12T17:32:55.690Z","1.0.0-beta.2":"2019-09-12T17:54:33.943Z","1.0.0-beta.20190918.4":"2019-09-18T15:51:15.007Z","1.0.0":"2019-09-18T16:05:22.024Z","2.0.0-beta.1":"2019-12-20T14:40:30.803Z","2.0.0-beta.2":"2019-12-20T16:46:08.759Z","2.0.0-beta.3":"2019-12-20T16:51:58.565Z","2.0.0-beta.4":"2019-12-20T16:56:28.609Z","2.0.0-beta.20200107.3":"2020-01-07T17:04:24.722Z","2.0.0":"2020-01-07T17:57:02.085Z","2.0.1-beta.20200116.2":"2020-01-16T16:55:09.488Z","2.0.1":"2020-01-16T16:56:44.054Z","2.0.2-beta.20200117.1":"2020-01-17T15:08:47.129Z","2.0.2":"2020-01-17T15:15:59.104Z","2.0.3-beta.20200130.2":"2020-01-30T22:34:54.832Z","2.0.3":"2020-01-30T22:36:45.282Z","2.0.4-beta.0":"2020-02-03T21:45:08.560Z","2.0.4-beta.1":"2020-02-03T21:49:43.186Z","2.0.4-beta.2":"2020-02-03T21:51:31.658Z","2.0.4-beta.3":"2020-02-03T22:08:11.425Z","2.0.4-beta.4":"2020-02-03T22:11:20.709Z","2.0.4-beta.5":"2020-02-03T22:22:07.152Z","2.0.4-beta.6":"2020-02-03T22:24:47.790Z","2.0.4-beta.7":"2020-02-03T22:26:16.890Z","2.0.4-beta.20200203.3":"2020-02-03T23:04:08.860Z","2.0.4":"2020-02-03T23:07:04.775Z","2.0.5-beta.20200211.2":"2020-02-11T17:36:07.338Z","2.0.5":"2020-02-11T18:25:11.827Z","2.0.6":"2020-02-21T12:46:57.280Z","2.0.6-beta.0":"2020-02-21T12:47:15.427Z","2.0.6-beta.1":"2020-02-21T12:55:27.637Z","2.0.6-beta.2":"2020-02-21T13:01:20.929Z","2.0.6-beta.20200221.2":"2020-02-21T15:08:37.847Z","2.0.7-beta.20200221.4":"2020-02-21T15:16:48.624Z","2.0.7":"2020-02-21T15:18:53.369Z","2.1.7-beta-es5":"2020-03-09T17:42:41.681Z","2.1.7-beta-es5-1":"2020-03-09T19:01:45.338Z","2.1.0-beta.20200327.5":"2020-03-27T17:26:24.703Z","2.1.0":"2020-03-27T17:27:59.729Z","2.1.1-beta.0":"2020-06-09T17:03:28.661Z","2.1.1-beta.1":"2020-06-09T17:06:09.300Z","2.1.1-beta.2":"2020-06-09T17:11:10.783Z","2.1.1-beta.3":"2020-06-09T17:25:17.699Z","2.2.0-beta.20200618.2":"2020-06-18T17:06:26.339Z","2.2.0-beta.20200618.4":"2020-06-18T17:24:36.717Z","2.2.0":"2020-06-18T17:31:25.787Z","2.2.0-beta.20201202.2":"2020-12-02T22:18:51.341Z","2.3.0-beta.20230307.3":"2023-03-07T21:11:09.202Z","2.3.0-beta.20230307.4":"2023-03-07T21:31:08.805Z","2.3.0-beta.20230307.5":"2023-03-07T21:38:11.120Z","2.3.0":"2023-03-07T21:39:58.370Z","2.3.1-beta.20240131.2":"2024-01-31T16:44:08.552Z","2.3.1-beta.20240131.3":"2024-01-31T17:04:33.442Z","2.3.1":"2024-01-31T18:51:08.096Z"},"maintainers":[{"name":"espnpm","email":"espnpm@perficient.com"},{"name":"darko.perficient","email":"darko.majkic@perficient.com"},{"name":"mikefrank","email":"mike.frank@perficient.com"},{"name":"shelby-prft","email":"shelby.hagman@perficient.com"},{"name":"curtis.swartzentruber","email":"c.swartzentruber@perficient.com"}],"description":"CLE Node Logging Library","keywords":[],"author":{"name":"Perficient, Inc."},"license":"ISC","readme":"﻿CLE Logger\n==\n\nA configurable javascript remote logging library, for the browser and Node.js, with built-in support for Twilio Flex UI.\n\n## Documentation\n\n- [Target Environment](#target-environment)\n- [Simple Example](#simple-example)\n- [Installation](#installation)\n- [Features](#features)\n- [Configuration](#configuration)\n- [API](#api)\n  - [Log sinks](#log-sinks)\n  - [Logging statements](#logging-statements)\n  - [Logging parameters](#logging-parameters)\n  - [Alternative logging functions](#alternative-logging-functions)\n  - [Logging constants](#logging-constants)\n  - [Log codes](#log-codes)\n  - [Registering Twilio Actions](#registering-twilio-actions)\n  - [Twilio Event Hooks](#twilio-event-hooks)\n  - [Version](#version)\n  - [Error To Object](#error-to-object)\n- [Log Object](#log-object)\n- [Handling PII Data](#handling-pii-data)\n- [Advanced Configuration](#advanced-configuration)\n  - [Remote logging](#remote-logging)\n- [Changelog](#changelog)\n\n## Target Environment\n\nAny Node.js environment capable of running and/or transpiling code compatible with Node.js v8.10 or above.\n\n### ES5 Compatible Version\n\nIf you need an ES5 compatible version (for example you need IE11 support), use this package: https://www.npmjs.com/package/@prftesp/cle-logger-es5. **This package is not regularly released with the main library, and should be used only when absolutely necessary**. If you need to support IE11, don't forget to include the necessary polyfills, mainly for `Promise` support. To do this, you can include this script tag on the page, before you load this library `<script src=\"https://cdn.polyfill.io/v3/polyfill.min.js?features=es6\"></script>`, or visit `https://polyfill.io` for more options.\n\n**Please note** that the ES5 version is generated, and is sharing the same code and documentation as the main library. This is why the rest of the documentation doesn't mention the ES5 version in text or examples. The only difference is how you reference it (`@prftesp/cle-logger` vs `@prftesp/cle-logger-es5`). To install it use `npm install @prftesp/cle-logger-es5`, and to import it use `import { cleLogger } from \"@prftesp/cle-logger-es5\";`.\n\n## Simple Example\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"console sink\",\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\tpiiSafe: true,\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n});\n\n// will log message to console\ncleLogger.info(\"log message\");\n```\n\n## Installation\n\n```bash\n$ npm install @prftesp/cle-logger\n```\n\n## Features\n\n- Runs in Node.js and the browser\n- Remote logging\n- Multiple log sinks\n- Support for tagging logs containing PII data to be delivered only to specified log sinks\n- Remote minimum log level overrides\n- Support for logging [Twilio Flex Actions](https://www.twilio.com/docs/flex/actions-framework) with debouncing for high volume\n- Optionally await logs to ensure requests complete in time before disposing resources (e.g. Twilio Functions)\n- Support for tagging logs with custom component names or correlation tokens\n- Send Twilio event hook data to a custom backend\n\n## Configuration\n\nTo configure the logger call the `init` function and pass in a configuration object. The library needs to be initialized and configured on page load, or in case of Node.js, in the main javascript file. Use in subsequent pages/components/files doesn't require configuration, the library behaves like a singleton.\n\n**Available options**\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// Twilio account sid, required for use with CLE\n\taccountSid: \"ACe3d7f8a771178fcerea4616445b0ed6l\",\n\n\t// Will be associated with all logs,\n\t// if not provided, a unique uuid will be generated\n\tcorrelationToken: \"b4b57b74-d3dc-4fd5-8e09-d4794c829f60\",\n\n\t// An array of that will receive an array of log objects,\n\t// and can optionally return a promise. Please see the\n\t// \"Log Sinks\" section for more information.\n\tlogSinks: [\n\t\t{\n\t\t\t// name of the log sink\n\t\t\tname: \"exampleConsoleSink\",\n\t\t\t// log handler function\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\t// This flag defines if the log message is processed by this sink when the contains\n\t\t\t// PII parameter of the log statement is true. piiSafe of true indicates all log\n\t\t\t// messages are allowed (even when contains PII is true); piiSafe of false indicates\n\t\t\t// only log messages with contains PII set to false will be processed by this log sink.\n\t\t\t// Defaults to false\n\t\t\tpiiSafe: true,\n\t\t\t// log level for the sink, defaults to INFO\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n\n\t// A way to display additional errors internal to the logger, useful for debugging, defaults to false\n\tlogErrorsToConsole: false,\n\n\t// If logging Twilio Flex Actions, provide a list of actions\n\t// to be excluded from logs\n\texcludedFlexActionNames: [\"SendTyping\"],\n\n\t// A custom endpoint to fetch log level overrides\n\t// NOTE: the provided url should always end with 'accountSid=',\n\t// the library will append the accountSid\n\tminLogLevelOverrideUrl: \"https://example.com/api/min-log-level?accountSid=\",\n\n\t// An instance of ('@twilio/flex-ui').Manager.\n\t// If provided, it will be used to get the worker sid and call sid (only during a call), and\n\t// automatically include it in 'customProperties' log property and with every log\n\ttwilioFlexManager: manager,\n\n\t// To send data from Twilio event hooks, to a custom backend,\n\t// configure one or more endpoints for each event type\n\ttwilioEventHooks: {\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\n\t},\n});\n```\n\n## API\n\nThe logger can be imported as an object in CommonJs or ES6 syntax:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n// or\nconst cleLogger = require(\"@prftesp/cle-logger\");\n```\n\nAlternatively, all top level members of the `cleLogger` are available as named exports and can be imported separately:\n\n```js\nimport { info, init, consoleLogger, logLevels, componentNames } from \"@prftesp/cle-logger\";\n\ninit({\n\tlogSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: logLevels.VERBOSE\n\t}],\n});\n\ninfo(\"log message\", { data: 123 }, null, componentNames.DASHBOARD);\n\n// vs\n\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ninit({\n\tcleLogger.logSinks: [{\n\t\tname: \"exampleConsoleSink\",\n\t\tlog: cleLogger.consoleLogger,\n\t\tpiiSafe: true,\n\t\tlogLevel: cleLogger.logLevels.VERBOSE\n\t}],\n});\n\ncleLogger.info(\"log message\", { data: 123 }, null, cleLogger.componentNames.DASHBOARD);\n```\n\n### Log sinks\n\nLog sinks are configurable log outputs, that enable you to send logs to multiple destinations in parallel. Log sinks can be provided via the `logSinks` config. The logSinks configuration defines an array of objects with each object defining the log sink activity. All provided log sinks will be executed in parallel. A log sink configuration must include the following properties:\n\n- `name`: this is an identifiable name for the logger and must be unique per log sink defined\n- `log`: this indicates the logic to process on each batch of logs processed by the sink. There are several built in processing functions that can be used or you can insert any code inline with this definition. The built in processing functions are `consoleLogger` which will send the log statements to the attached console or `remoteLogger` which will send the log statements to the specified url provided as a definition on the parameter (i.e. `remoteLogger(\"https://mylogger.url\"`). For a custom log processing implementation, the logic must be async operations and must return a Promise object. The custom log method will receive an array of log objects for you to handle. You can find out more about the log object type in the [Log Object](#log-object) section. Here is an example of a custom log function:\n\n```js\nlogs => {\n\t// log each received log to the console separately\n\tlogs.forEach(log => console.log(log));\n\treturn Promise.resolve();\n};\n```\n\n- `logLevel`: defines the minimum default log level that will be processed by this log sink. Log levels lower than the minimum level will not be processed by this log sink. The minimum default log level can be overridden at run time by the value returned from the overall `minLogLevelOverrideUrl` that is set up in the library `init`.\n- `piiSafe`: indicates if log messages with a `containsPII` parameter value of true should be processed by the log sink. If `containsPII` parameter value on the log message is true and piiSafe is false then the log message will not be processed by this log sink, if piiSafe is true then it will. If the `containsPII` parameter value is false on the log message than it is always processed by the log sink regardless of the piiSafe value.\n\nCustom log sink configuration example. In this example, the custom code executed for each log statement is to append the log into a file. Note how a Promise is returned as required:\n\n```js\n{\n\t// name of the log sink\n\tname: \"exampleConsoleSink\",\n\t// log handler function - this is where you put your custom handling code and\n\t// remember to always return a promise. If your operation is not asynchronous,\n\t// you can do this by returning Promise.resolve().\n\tlog: logs => {\n\t\tfs.appendFile(\n\t\t\t\"./log.txt\",\n\t\t\t`${JSON.stringify(logs)}`,\n\t\t\tconsole.log\n\t\t);\n\t\treturn Promise.resolve();\n\t},\n\t// should this log sink accept PII data, defaults to false\n\tpiiSafe: true,\n\t// log level for the sink, defaults to INFO\n\tlogLevel: cleLogger.logLevels.VERBOSE\n}\n```\n\nExample log sink configuration that uses a custom coded sink and both standard console and remote logging sinks:\n\n```js\nconst fs = require(\"fs\");\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"fileSink\",\n\t\t\tlog: logs => {\n\t\t\t\tfs.appendFile(\"./log.txt\", `${JSON.stringify(logs)}`, console.log);\n\t\t\t\treturn Promise.resolve();\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"remoteSink\",\n\t\t\tlog: cleLogger.remoteLogger(\"https://my.log.url\"),\n\t\t\tlogLevel: cleLogger.logLevels.WARN,\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"consoleSink\",\n\t\t\tlog: cleLogger.consoleLogger,\n\t\t\tlogLevel: cleLogger.logLevels.VERBOSE,\n\t\t},\n\t],\n});\n```\n\n### Logging statements\n\nTo create logs, you can use one of the logging function available. They all take the same arguments, described below, and log at a corresponding log level. For example, if you use the `cleLogger.verbose` logging function, and your log sink is configured with `logLevel: cleLogger.logLevels.VERBOSE`, that log sink will receive the log. If your log sink is configured with a higher log level, for example `logLevel: cleLogger.logLevels.INFO`, then it would not receive the verbose log.\n\nHere are the available logging functions:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(...);\ncleLogger.info(...);\ncleLogger.warn(...);\ncleLogger.error(...);\ncleLogger.critical(...);\n```\n\nThe functions are all async, return `Promise<void>`, and can be optionally awaited. The suggested approach is to await log functions in environments where there is low risk of resources being disposed before logging completes (e.g. browser, custom Node.js backends). It is suggested not to await logs in serverless environments (e.g. Twilio Functions, AWS Lambdas), that have limited execution time.\n\n### Logging parameters\n\nAll logging functions take the same parameters:\n\n- `message`: the only required property, no logs will be generated without it\n  - type: string\n  - required: yes\n- `additionalData`: any additional log data you would like to include with the log, can be any data type\n  - type: any\n  - required: no\n  - default value: null\n- `customProperties`: a single level deep object of important properties you would like to distinguish from general log data, like various sids CLE uses it to index these properties in Application Insights. For CLE, try to use keys found in `cleLogger.customPropertyKeys`\n  - type: object\n  - required: no\n  - default value: {}\n- `componentName`: another useful way to 'tag' your logs for easier searching. Can be any string, but for CLE, try to use available options in `cleLogger.componentNames`\n  - type: string\n  - required: no\n  - default value: \"\"\n- `logCode`: log code, can be any number used to categorize logs. For defined ranges see the [log codes](#log-codes) section.\n  - type: number\n  - required: no\n  - default value: 0\n- `correlationToken`: correlation token to be associated with this log only\n  - type: string\n  - required: no\n  - default value: token provided in `init`, or a generated token if one wasn't provided\n- `containsPII`: does the log contain PII data, defaults to false. If set to true, the log will be passed in only to those log sinks that have `piiSafe` set to true. For more info see the [handling PII data](#handling-pii-data) section.\n  - type: boolean\n  - required: no\n  - default value: false\n\n### Alternative logging functions\n\nIf you would prefer to pass in an object to logging functions, instead of using a long list of parameters, you can use the new logging functions available under the `cleLogger.v2` namespace. These functions are namespaced to keep backwards compatibility, but you should feel free to use them going forward. The behavior of these functions is exactly the same as with those listed in [logging statements](#logging-statements) section.\n\nExample verbose logging statement with both versions of the logging function:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.verbose(\n\t\"Log message\", // message\n\t{ data: \"test\" }, // additionalData\n\t{ [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" }, // customProperties\n\tcleLogger.componentNames.SESSION, // componentName\n\t70123, // logCode\n\t\"my-token\", // correlationToken\n\ttrue, // containsPII\n);\n\ncleLogger.v2.verbose(\n\t\"Log message\",\n\t{\n\t\tadditionalData: { data: \"test\" },\n\t\tcustomProperties: { [cleLogger.customPropertyKeys.CALL_SID]: \"my-call-sid\" },\n\t\tcomponentName: cleLogger.componentNames.SESSION,\n\t\tlogCode: 70123,\n\t\tcorrelationToken: \"my-token\",\n\t\tcontainsPII: true,\n\t}\n);\n```\n\nAs you can see in the examples above, both functions take in the same values, but the `v2` version makes it a bit easier to see which value corresponds to each key. It comes down to personal preference, use whichever you prefer.\n\n### Logging constants\n\n**Logging levels**\n\nLogging level constants should be used when configuring the log level of a [log sink](#log-sinks).\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.logLevels.VERBOSE; // verbose\ncleLogger.logLevels.INFO; // info\ncleLogger.logLevels.WARN; // warn\ncleLogger.logLevels.ERROR; // error\ncleLogger.logLevels.CRITICAL; // critical\n```\n\n**Component names**\n\nDefines a set of commonly used components that should be used for the `componentName` parameter value of a log statement if you are logging from one of these common components in a Twilio implementation.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.componentNames.SESSION;\ncleLogger.componentNames.DIALER;\ncleLogger.componentNames.DASHBOARD;\ncleLogger.componentNames.AFTERCALL;\ncleLogger.componentNames.SUPERVISOR;\n```\n\n**Custom property keys**\n\nDefines a set of commonly used key that should be used when defining the custom properties object that is supplied in the `customProperties` parameter of any logging statement.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.customPropertyKeys.CHANNEL_SID;\ncleLogger.customPropertyKeys.TASK_SID;\ncleLogger.customPropertyKeys.CALL_SID;\ncleLogger.customPropertyKeys.WORKFLOW_SID;\ncleLogger.customPropertyKeys.WORKER_SID;\n```\n\n### Log codes\n\nIt can be helpful when investigating issues to only view logs related to specific areas of a system. Use Log Codes to give a category definition for what area of the contact center the log activity occurred within. Use numbers within the following pre-defined log code ranges or define custom log codes by using your own numbers higher than 300,000.\n\n- 100 000 - 109 999 => CALL_STATUS\n- 110 000 - 119 999 => IVR\n- 120 000 - 129 999 => ROUTING\n- 130 000 - 139 999 => AGENT_ACTION\n- 140 000 - 149 999 => TRANSFER\n- 150 000 - 159 999 => RM_INTEGRATION\n- 160 000 - 169 999 => WFM_INTEGRATION\n- 170 000 - 179 999 => CUSTOM_INTEGRATION\n- 180 000 - 189 999 => SUPERVISOR_ACTIVITY\n\n### Registering Twilio Actions\n\nIt is important to log information from the Twilio Flex UI for all implementations even when there is little customization happening. Many developers find it useful to generate log activity when some, or any, of the Twilio Actions occur within the Twilio Flex UI. Twilio Actions are events that arise from the Twilio Flex UI base implementation and indicate when certain activity is occurring - such as an agent hanging up a call. The CLE Logger library can be setup to automatically register all, or selected, Twilio Actions such that right after any code tied to the Action event has been executed a log statement is written indicating the Action occurred. This log statement will also automatically include the action event payload. To enable this logging capability, use the following approach to register the Twilio Actions. This only needs to be performed once during the initialization of the Twilio Flex UI page on the client.\n\nExample in a Twilio Flex UI plugin:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n/**\n * @param flex { typeof import('@twilio/flex-ui') }\n * @param manager { import('@twilio/flex-ui').Manager }\n */\ninit(flex, manager) {\n\tcleLogger.init({\n\t\taccountSid: \"your-account-sid\"\n\t});\n\n\tcleLogger.registerTwilioActions(flex);\n}\n```\n\n**Note**: A Twilio account sid must be provided during `init` with the `accountSid` parameter, otherwise the actions won't be logged.\n\nThe logger will loop through all actions found under `flex.Actions.actions`, and attach `after` event handlers to log the action name and payload. Any private properties found in the payload (starting with '\\_') will be stripped out before logging. As these logs can be frequent, they will be sent in batches of 10 or every second, whichever comes first. Not all Action events need to be logged and by using the `excludedFlexActionNames` property in the logger configuration the Action events not desired can be ignored. Any actions names configured in `excludedFlexActionNames` will not be included in the logs.\n\n**Important** Registering Twilio Actions must be done once per page load, otherwise duplicate actions will be registered. When using multiple Twilio Flex UI plugins, it's recommended to register Twilio Actions only in one of the plugins, or use a dedicated plugin for this purpose.\n\n**Twilio Actions log levels**\n\nBelow is a list of associated log levels for known Twilio Actions, any unknown action will have a log level of `INFO`.\n\n```js\nAcceptTask: INFO;\nCancelTransfer: INFO;\nCompleteTask: INFO;\nHangupCall: INFO;\nHideDirectory: VERBOSE;\nHistoryGo: VERBOSE;\nHistoryGoBack: VERBOSE;\nHistoryGoForward: VERBOSE;\nHistoryPush: VERBOSE;\nHistoryReplace: VERBOSE;\nHoldCall: INFO;\nHoldParticipant: INFO;\nKickParticipant: INFO;\nLogout: INFO;\nMonitorCall: INFO;\nNavigateToView: INFO;\nRejectTask: INFO;\nSelectTask: INFO;\nSelectTaskInSupervisor: INFO;\nSelectWorkerInSupervisor: INFO;\nSendMessage: INFO;\nSendTyping: VERBOSE;\nSetActivity: INFO;\nSetInputText: VERBOSE;\nShowDirectory: VERBOSE;\nStopMonitoringCall: INFO;\nToggleMute: INFO;\nToggleSidebar: VERBOSE;\nTransferTask: INFO;\nUnholdCall: INFO;\nUnholdParticipant: INFO;\nWrapupTask: INFO;\n```\n\n### Twilio Event Hooks\n\nCurrently, the Twilio console only supports adding a single hook for events such as debugger and task router. To support proxying data to additional backends, you can configure custom hook urls with `twilioEventHooks` option, and use the following functions to send event data to a custom endpoint.\n\n**NOTE:** this assumes you have a Node.js API to register as hooks with Twilio. You can then use this package in your API to proxy the event data to other endpoints. The content type of data sent by the logger is `application/x-www-form-urlencoded` to be consistent with the format Twilio is using. The data sent with the logger should be exactly what your api received from Twilio.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.init({\n\t// unrelated init properties omitted from this example.\n\t// include in the twilioEventHooks array an entry for only the event hooks\n\t// the executing code is going to relay. For example, if the code is only\n\t// handling task router web hook events then you would only need to define\n\t// the taskRouter relay url here.\n\ttwilioEventHooks: {\n\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\tcallStatus: [\"https://example.com/twilio-call-status-hook?key=123\"],\n\t},\n});\n\ncleLogger.sendTwilioDebuggerEvents(twilioDebuggerEventData);\ncleLogger.sendTwilioTaskRouterEvent(twilioTaskRouterEventData);\ncleLogger.sendTwilioCallStatusEvent(twilioCallStatusEventData);\n```\n\nExample Azure Function hook for debugger events:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\n\nmodule.exports = async function(context, req) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\tdebugger: [\"https://example.com/twilio-debugger-hook?key=123\"],\n\t\t},\n\t});\n\n\t// log to console\n\tcleLogger.info(\"Received Twilio debugger event\");\n\n\t// send event data to a configured url\n\tawait cleLogger.sendTwilioDebuggerEvents(context.req.body);\n};\n```\n\n**NOTE WHEN USING THIS FUNCTIONALITY IN TWILIO FUNCTIONS:** if using Twilio functions to setup the relay, an additional step is needed to ensure the correct format is sent. Right now, Twilio function will automatically convert the event data to json, and there doesn't seem to be a way to get the raw request body. To work around this, you need to manually convert the json event data back to `x-www-form-urlencoded` format.\n\nExample Twilio function task router relay using the `form-urlencoded` npm package to convert the data to `x-www-form-urlencoded` format:\n\n```js\nconst cleLogger = require(\"@prftesp/cle-logger\");\nconst formurlencoded = require(\"form-urlencoded\").default;\n\nexports.handler = async function(context, event, callback) {\n\tcleLogger.init({\n\t\ttwilioEventHooks: {\n\t\t\ttaskRouter: [\"https://example.com/twilio-task-router-hook?key=123\"],\n\t\t},\n\t});\n\n\tawait cleLogger.sendTwilioTaskRouterEvent(formurlencoded(event));\n\n\tcallback();\n};\n```\n\n### Version\n\nVersion will be automatically included in all logs.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ncleLogger.version; // 1.0.0\n```\n\n### Error To Object\n\nUtility function to convert an `Error` object to a plain JS object, with all properties preserved. When serialized, `Error` objects will omit custom properties from the output which makes debugging difficult. This utility will traverse all properties in the `Error` object, and output a plain JS object with all properties including the stack trace. See an example below.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\nconst err = new Error(\"error message\");\nconsole.log(err); // \"Error: error message\\n    at Object.<anonymous> …\"\n\nconst errObject = cleLogger.errorToObject(err);\nconsole.log(errObjct); // {name: 'Error', message: 'error message', stack: 'Error: error message\\n    at Object.<anonymous> …'}\n```\n\nExample usage when logging errors:\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\ntry {\n\t// some code that can throw an error\n} catch (error) {\n\tcleLogger.error(\"My error message\", cleLogger.errorToObject(error));\n}\n```\n\n## Log Object\n\nThe following is the type definition of the log object received by [log sinks](#log-sinks). It consists of properties provided in log messages, as well as some that are automatically inserted like the `eventDate`. You can use this information to customize your logs inside a custom log sink (e.g. filter out unwanted logs, modify some of the properties to include more data, or any kind of conditional logic based on the value of these properties). If using the built in `remoteLogger` log processor with your custom backend, you can expect an array of these objects to be sent to your log endpoint. For more info about implementing a custom logging endpoint see the [remote logging](#remote-logging) section.\n\n```js\n{\n\t// Account sid if provided during init\n\taccountSid: string;\n\n\t// Any additional log data to be included with the log\n\tadditionalData: unknown;\n\n\t// Component name, if provided\n\tcomponentName: string;\n\n\t// Generated or configured token\n\tcorrelationToken: string;\n\n\t// Current UTC datetime in ISO 8601 format\n\teventDate: string;\n\n\t// Log level\n\tlevel: string;\n\n\t// Log code, if provided\n\tlogCode: number;\n\n\t// Log message\n\tmessage: string;\n\n\t// A single level deep object of important properties you would\n\t// like to distinguish from general log data, like various sids\n\tproperties: object;\n\n\t// BrowserFunction if the environment is the browser\n\t// CloudFunction if the environment is Node.js\n\tsource: string;\n\n\t// Indicates that the log contains PII data\n\tcontainsPII: boolean;\n\n\t// Logging library version\n\tloggerVersion: string;\n}\n```\n\n## Handling PII Data\n\nThe logger provides support for helping to protect log messages or data that need to include PII (Personally Identifiable Information). While the logger has several mechanisms that can be used to flag logs that contain PII and block PII from going to particular log sinks, it is still ultimately the developers responsibility to ensure PII is flagged or marked appropriately for the logger to know when it exists - the logger cannot automatically detect if PII is part of a log statement or data object. The general guideline is that developers should avoid including any PII data in log information whenever possible. In the instances where PII data must be included there are several mechanisms the logger supports to help indicate PII data and protect where it is sent.\n\nA `containsPII` parameter exists on log statement methods. This flag should be set to true if there is PII data, or any potential chance of it, in the log message, additional data or custom properties data. When this flag is set to true then it will only allow log sinks that have the `piiSafe` configuration set to true to receive and process the log message. `piiSafe` indicates on a log sink whether that log sink receiver is able to internally store PII data OR has logic to strip PII data that is tagged in the message or from pre-configured additional data fields before the log is written to any storage or output device. When sending data to any `remoteLogger` endpoint the log sink can be marked `piiSafe` only as long as data is appropriately tagged in messages and/or the names of custom properties that might have PII values in additional data are pre-configured AND the endpoint implementer has verified and ensured their receiving code will remove marked PII data immediately before sending logs to any output or storage media.\n\nTo tag any part of the message parameter in a log statement as containing PII within the actual log message use the `tagPII` utility. The `tagPII` utility will indicate to a remote backend that the text between the tags should be stripped or masked before sending logs to any output or storage media.\n\nExample tagged output:\n\n```js\nimport { tagPII } from \"@prftesp/cle-logger\";\n\nconst msg = `message sent to ${tagPII(\"user@example.com\")}`; // message sent to _PII__user@example.com__PII_\n```\n\n## Advanced Configuration\n\n### Remote logging\n\nThe library provides a built in way to transmit logs to a remote http endpoint using the `remoteLogger` logs processor. The `remoteLogger` can be directed at a known listener such as CLE or you can create your own custom log listener. In addition, the log level in effect can be controlled remotely setting the `minLogLevelOverrideUrl` to an http address that will return the current log level to use instead of any defined default logLevel on each `logSink`. The `minLogLevelOverrideUrl` can point to a known central API such as CLE or you can create your own endpoint.\n\nThe following sections outline how to create your own endpoints for either receiving log messages from the `remoteLogger` log sink or providing the current log level override to it.\n\n**Remote log endpoint**\n\nTo implement your own remote logging endpoint, all you have to do is accept `POST` requests, with a JSON payload of an array of [log objects](#log-object), and return either `{ success: true }` or `{ success: false }`. The endpoint should always return a `200 OK` status even if your server code fails so that the CLE logger does not cause failures in the business logic in which it is embedded. A good use-case would be leveraging a serverless api platform to output logs to a queue.\n\nExample of an Azure Function that can receive requests from the `remoteLogger` log sink:\n\n```js\nmodule.exports = async function(context, req) {\n\tcontext.log(req.body); // body will have the log array\n\n\tcontext.res = {\n\t\tstatus: 200,\n\t\tbody: { success: true },\n\t};\n};\n```\n\n**Remote log level override**\n\nThe `minLogLevelOverrideUrl` configuration property is used to provide an endpoint that accepts `GET` requests and a `accountSid` query string parameter. `accountSid` can be used to scope the log level override to a particular account. The return value of the request should be a valid log level or null. In cases when a valid log level is returned, it will override the `minLogLevel` configuration property. If null is returned, the `minLogLevel` value will be used. The call to this endpoint will be made during the initialization phase. The endpoint should always return a `200 OK` status code and use the `success` property to indicate failure.\n\nExample success response:\n\n```json\n{\n\t\"success\": true,\n\t\"minLogLevel\": \"warn\"\n}\n```\n\nExample error response:\n\n```json\n{\n\t\"success\": false,\n\t\"minLogLevel\": null\n}\n```\n\n## Changelog\n\n### v2.3.0\n\n**New features**\n\n- To address long-running log requests caused by occasional slow remote log responses, this version introduces default timeouts for log requests. Timeout for Twilio event hooks is 2000 ms and is not configurable. Timeout for the `remoteLogger` helper is 500 ms and can be adjusted with an optional second parameter: `remoteLogger(\"https://mylogger.url\", 1000);`\n\n### v2.2.0\n\n**New features**\n\n- Added [alternative logging functions](#alternative-logging-functions) that have a much shorter parameter list, and allow you to specify only those logging properties that you want, without the need to pass in `null` for parameters you are not interested in. New functions are available under `cleLogger.v2` for backward compatibility reasons.\n\n```js\nimport { cleLogger } from \"@prftesp/cle-logger\";\n\n// if you want to log just the message and the token, \n// you need to default parameters to null until you get to token\ncleLogger.verbose(\"Log message\", null, null, null, null, \"my-token\");\n\n// with the new functions, you just provide an object with the properties you want\ncleLogger.v2.verbose(\"Log message\", { correlationToken: \"my-token\" });\n```\n\n### v2.1.0\n\n**New features**\n\n- Added [Error To Object](#error-to-object) method to assist users in converting error object to plain objects.\n\n### v2.0.7\n\n**New features**\n\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the call sid during a call, and automatically include it in `customProperties` log property for all logs that happen during that call (including Flex Action logs).\n\n### v2.0.5\n\n**New features**\n\n- If an instance of the `twilioFlexManager` is provided during initialization, it will be used to get the worker sid and automatically include it in `customProperties` log property.\n\n**Other updates**\n\n- Updated documentation based on user feedback.\n\n### v2.0.4\n\n**Bug fixes**\n\n- Fixed issue with logging to console in Twilio Functions. The `consoleLogger` will now output the log object as serialized json.\n\n### v2.0.0 [2019-12-20]\n\n**New features**\n\n- All logging output is now configured using the `logSinks` property. The library provides two commonly used loggers - `consoleLogger` and a `remoteLogger` that accepts the log url as a parameter. [Configuration](#configuration)\n- Logging level is now configured per log sink. For example, you might want to have verbose console logging, but only send information logs to a remote log output. [Log sinks](#log-sinks)\n- Log functions have a new optional `containsPII` parameter, that can be used to flag a log as PII. [Logging parameters](#logging-parameters)\n- A new helper function `tagPII` is available to help mark PII in log messages. This works for custom log objects as well. [Handling PII Data](#handling-pii-data)\n- PII handling - log sinks can be configured as PII safe, meaning only PII safe log sinks will receive logs flagged as PII. [Log sinks](#log-sinks)\n\n**Breaking changes**\n\n- `customLogSink` configuration renamed to `logSinks`\n- Removed `disableRemoteLogging` configuration. Now that all log output is configured with log sinks, you can conditionally remove a log sink if needed.\n- Removed `logToConsole` configuration. This is now configured as a log sink with a `consoleLogger` helper function.\n- Removed `logUrl` configuration. This is now configured as a log sink with a `remoteLogger` helper function.\n- Removed `minLogLevel` configuration. This is now configured per log sink.\n\n**Migration guide**\n\n```js\nimport cleLogger from \"@prftesp/cle-logger\";\n\n// v1.0.0 and older config\ncleLogger.init({\n\tdisableRemoteLogSinks: true, // no direct replacement\n\tlogToConsole: true, // replaced by the \"console logger\" sink\n\tlogUrl: \"http://log.output/api\", // replaced by the \"remote logger\" sink\n\tminLogLevel: cleLogger.logLevels.INFO, // this is now configured per log sink\n\tcustomLogSinks: [\n\t\t{\n\t\t\tname: \"custom sink\",\n\t\t\tlog: logs => {\n\t\t\t\t/* your custom logging */\n\t\t\t},\n\t\t},\n\t],\n});\n\n// v2.0.0 config\ncleLogger.init({\n\tlogSinks: [\n\t\t{\n\t\t\tname: \"custom sink\",\n\t\t\tlog: logs => {\n\t\t\t\t/* your custom logging */\n\t\t\t},\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"console logger\",\n\t\t\tlog: consoleLogger,\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t\t{\n\t\t\tname: \"remote logger\",\n\t\t\tlog: remoteLogger(\"http://log.output/api\"),\n\t\t\tlogLevel: cleLogger.logLevels.INFO,\n\t\t\t// set to true to get feature parity with older version,\n\t\t\t// please consider your use case before setting\n\t\t\tpiiSafe: true,\n\t\t},\n\t],\n});\n```\n\n### v1.0.0 [2019-09-12]\n\n- Initial release\n","readmeFilename":"README.md"}