{"_id":"bandeng-logger","_rev":"55-19a819c6072b3a3ee0fc81b5aacf4a74","name":"bandeng-logger","dist-tags":{"latest":"1.4.7"},"versions":{"1.0.0":{"name":"bandeng-logger","version":"1.0.0","author":{"name":"lizhi"},"license":"ISC","_id":"bandeng-logger@1.0.0","maintainers":[{"name":"ty4spd","email":"cblzlz@hotmail.com"}],"dist":{"shasum":"c91f7a27643d96bfe19396877e228accafefecbc","tarball":"https://registry.npmjs.org/bandeng-logger/-/bandeng-logger-1.0.0.tgz","fileCount":3,"integrity":"sha512-wjXqWdpgorOgpKeBcP95hZGLKyookG3tVVIS2KCPgB398SJFNLX9AsTHWW0BAuHuarRIfB2s8SEjtrNKaWjdBQ==","signatures":[{"sig":"MEQCIH1h6xhKZJH2VB3MPYHRlCoouCfmaMIcjUS1tvBfS0LjAiAY/yzCGZ/MeJS1UxAHQwoz8rOtRfrsPMG+e9bMKdaCoQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":32428},"main":"index.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"_npmUser":{"name":"ty4spd","email":"cblzlz@hotmail.com"},"_npmVersion":"8.19.4","description":"logger from bandeng developer team.","directories":{},"_nodeVersion":"16.20.2","dependencies":{"rotating-file-stream":"^3.2.6"},"_hasShrinkwrap":false,"devDependencies":{},"_npmOperationalInternal":{"tmp":"tmp/bandeng-logger_1.0.0_1748859432297_0.2743040438471622","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"bandeng-logger","version":"1.0.1","author":{"name":"lizhi"},"license":"ISC","_id":"bandeng-logger@1.0.1","maintainers":[{"name":"ty4spd","email":"cblzlz@hotmail.com"}],"dist":{"shasum":"861bd6879739d7d7059fdb02146a9c75ae896462","tarball":"https://registry.npmjs.org/bandeng-logger/-/bandeng-logger-1.0.1.tgz","fileCount":3,"integrity":"sha512-J1gRVeWi1ZreiALYFMtcCBq7mqhP1iVEmSFppvXv3Dz2qf8EnOCL8+4FHqbvETvqoT7h3K3JieHzxsSkZ/McVg==","signatures":[{"sig":"MEUCIBPMZ3nG/Jftr/Yxgvkpls0Fnp8cpx7KZyDKkzb4N14bAiEA/MgedtLlZK8avvDrE50eSJjqqRcUYknrQL9ItO+Nemo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":34729},"main":"index.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"_npmUser":{"name":"ty4spd","email":"cblzlz@hotmail.com"},"_npmVersion":"8.19.4","description":"logger from bandeng developer team.","directories":{},"_nodeVersion":"16.20.2","dependencies":{"rotating-file-stream":"^3.2.6"},"_hasShrinkwrap":false,"devDependencies":{},"_npmOperationalInternal":{"tmp":"tmp/bandeng-logger_1.0.1_1748915149323_0.1455042906146693","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"bandeng-logger","version":"1.0.2","author":{"name":"lizhi"},"license":"ISC","_id":"bandeng-logger@1.0.2","maintainers":[{"name":"ty4spd","email":"cblzlz@hotmail.com"}],"dist":{"shasum":"0b4c77485233c91c95326f6c2cebef50613cab3c","tarball":"https://registry.npmjs.org/bandeng-logger/-/bandeng-logger-1.0.2.tgz","fileCount":4,"integrity":"sha512-MOmhijm8TNpnK2VQqBRcCjvBAFpKzugLaOhzRbqWoZ14uHweGX+lIp9ybfBmswFJjAuzgq2egJEa8oaPN3oALA==","signatures":[{"sig":"MEUCIQDkUUmFFUYW7akjSMYk7/kq9WLDtBoWHBsujfzURr/abgIgUUy8lepNc29Sfji3wkV1y9QGOnVloi/28LwYVdx0Miw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":40129},"main":"index.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"_npmUser":{"name":"ty4spd","email":"cblzlz@hotmail.com"},"_npmVersion":"8.19.4","description":"logger from bandeng developer team.","directories":{},"_nodeVersion":"16.20.2","dependencies":{"rotating-file-stream":"^3.2.6"},"_hasShrinkwrap":false,"devDependencies":{},"_npmOperationalInternal":{"tmp":"tmp/bandeng-logger_1.0.2_1749633280499_0.4070136044167407","host":"s3://npm-registry-packages-npm-production"}},"1.0.3":{"name":"bandeng-logger","version":"1.0.3","author":{"name":"lizhi"},"license":"ISC","_id":"bandeng-logger@1.0.3","maintainers":[{"name":"ty4spd","email":"cblzlz@hotmail.com"}],"dist":{"shasum":"db3c70e4177a50d38e762cb1c3ae3bb225b1cf0e","tarball":"https://registry.npmjs.org/bandeng-logger/-/bandeng-logger-1.0.3.tgz","fileCount":4,"integrity":"sha512-tCcoQEFQPt84eoaGquf6rptFF1V7LEGlKeObgnArhLurjB6+GQ498G6AoPjRIPf/tchRCbRXVUhFKVtacjF19Q==","signatures":[{"sig":"MEQCIDOZ3RoX7Z6ZyeIv2i5vmRvojKsSp5XztRtpd0ViFU10AiBL1tITIsQcQORu2wgxpeJVhoxwygDwzq9Fb5cEm7aFYA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":40373},"main":"index.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"_npmUser":{"name":"ty4spd","email":"cblzlz@hotmail.com"},"_npmVersion":"8.19.4","description":"Logger from bandeng developer team.","directories":{},"_nodeVersion":"16.20.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/bandeng-logger_1.0.3_1749697865603_0.48849986412091684","host":"s3://npm-registry-packages-npm-production"}},"1.0.4":{"name":"bandeng-logger","version":"1.0.4","author":{"name":"lizhi"},"license":"ISC","_id":"bandeng-logger@1.0.4","maintainers":[{"name":"ty4spd","email":"cblzlz@hotmail.com"}],"dist":{"shasum":"b952010bb4119ab25f9e3ef292ef0ffcdbb5f1ee","tarball":"https://registry.npmjs.org/bandeng-logger/-/bandeng-logger-1.0.4.tgz","fileCount":4,"integrity":"sha512-6Ww+AIA2zhsgxDBYQd+Vj0chIpl4NK7GLeQ2QXtbPhPFpgYhgz5EzjuoYZJgOEewmIfTrWqaUjxNdYFolbFzzw==","signatures":[{"sig":"MEUCIHWRJ21QXE7pHi6Ap/Gc6Rs199MzIZQTT9syA+8fN1CqAiEAiTaz373au+NQe1dlTla5vH/X334aZ2tta4pM3PxOetg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":50650},"main":"index.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"_npmUser":{"name":"ty4spd","email":"cblzlz@hotmail.com"},"_npmVersion":"8.19.4","description":"Logger from bandeng developer team.","directories":{},"_nodeVersion":"16.20.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/bandeng-logger_1.0.4_1749714597452_0.8394083053567691","host":"s3://npm-registry-packages-npm-production"}},"1.0.5":{"name":"bandeng-logger","version":"1.0.5","author":{"name":"lizhi"},"license":"ISC","_id":"bandeng-logger@1.0.5","maintainers":[{"name":"ty4spd","email":"cblzlz@hotmail.com"}],"dist":{"shasum":"e248cfcf983e10a630149fa10a0855b9b5458a3d","tarball":"https://registry.npmjs.org/bandeng-logger/-/bandeng-logger-1.0.5.tgz","fileCount":4,"integrity":"sha512-Jq93Jmm7B1YDOk/Utkn48U0e/8Ceoz47MivVGXndr+l8Ap6ul6Th4PKGUsSyp9siaCMhj5miD4Q2RwhP6U5N9g==","signatures":[{"sig":"MEYCIQCaYI4/0hkR9Aq6M3cUUMizev5oHnzScu0SyvZJsSDatAIhAOS9+fqBhkl8+hqHADhO3wQ6D8fKF4O0MA/ZKKoSD8VG","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":53934},"main":"index.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"_npmUser":{"name":"ty4spd","email":"cblzlz@hotmail.com"},"_npmVersion":"8.19.4","description":"Logger from bandeng developer team.","directories":{},"_nodeVersion":"16.20.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/bandeng-logger_1.0.5_1749882743025_0.16233531837467385","host":"s3://npm-registry-packages-npm-production"}},"1.0.6":{"name":"bandeng-logger","version":"1.0.6","author":{"name":"lizhi"},"license":"ISC","_id":"bandeng-logger@1.0.6","maintainers":[{"name":"ty4spd","email":"cblzlz@hotmail.com"}],"dist":{"shasum":"3c1e8c47b79981ee8aaa3664933d7137486c884f","tarball":"https://registry.npmjs.org/bandeng-logger/-/bandeng-logger-1.0.6.tgz","fileCount":3,"integrity":"sha512-e4ppraJju5QH9stIxykUw3GnYmkRl8X6icdfGrBeRDMxoFVcKCvjP9z0XpE+kHQjpTwTe0N/H4oA6uMyp5ETaw==","signatures":[{"sig":"MEUCIDYrNn3+nweRRp9scfotY1x+Ky5Ljf4a2oOeisYtA/k9AiEAyy0joQlT/NBqnOA0w4NO3BuVTDOJ6qWm/BWzKq4AQCk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":54389},"main":"index.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"_npmUser":{"name":"ty4spd","actor":{"name":"ty4spd","type":"user","email":"cblzlz@hotmail.com"},"email":"cblzlz@hotmail.com"},"_npmVersion":"8.19.4","description":"Logger from bandeng developer team.","directories":{},"_nodeVersion":"16.20.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/bandeng-logger_1.0.6_1750731099366_0.07245085024150799","host":"s3://npm-registry-packages-npm-production"}},"1.0.7":{"name":"bandeng-logger","version":"1.0.7","author":{"name":"lizhi"},"license":"ISC","_id":"bandeng-logger@1.0.7","maintainers":[{"name":"ty4spd","email":"cblzlz@hotmail.com"}],"dist":{"shasum":"337cbc28a0fd675f3d912274c80c6ca284f8a12e","tarball":"https://registry.npmjs.org/bandeng-logger/-/bandeng-logger-1.0.7.tgz","fileCount":3,"integrity":"sha512-q0XpTwVUWwgEmYhIF9TWLvrsmPuiRsZYzmdRusVZ3GOdz5QkAcFGz6H/WIZ+lCk/ZJsTGKd1Evdv3Ui4hhMkQw==","signatures":[{"sig":"MEYCIQCMUaRS+RSOAxb8mRU6wp/ZwtVG2kAqdeTTN/wpsBwfjQIhAM/ABDyXil94hMwamogicZg9cdHq5I7g4ZOM06TPzfPD","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":61768},"main":"index.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"_npmUser":{"name":"ty4spd","actor":{"name":"ty4spd","type":"user","email":"cblzlz@hotmail.com"},"email":"cblzlz@hotmail.com"},"_npmVersion":"8.19.4","description":"Logger from bandeng developer team.","directories":{},"_nodeVersion":"16.20.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/bandeng-logger_1.0.7_1750844045032_0.1886991845219319","host":"s3://npm-registry-packages-npm-production"}},"1.0.8":{"name":"bandeng-logger","version":"1.0.8","author":{"name":"lizhi"},"license":"ISC","_id":"bandeng-logger@1.0.8","maintainers":[{"name":"ty4spd","email":"cblzlz@hotmail.com"}],"dist":{"shasum":"5fe7af0ce1a1a7bce48d51dd3ea83f1ff9665c2d","tarball":"https://registry.npmjs.org/bandeng-logger/-/bandeng-logger-1.0.8.tgz","fileCount":3,"integrity":"sha512-W5BKGdOdlpPmvN2mn1Qu3hNmq44Z1f+9eBmA5CFPZJ7NAsxhJxWICJCBvap4WmoWJ8iNo5u/6T8/Fz44658+9Q==","signatures":[{"sig":"MEQCIA/ujvrCsZW6zV1XplodBPP5+VWuGBeekxpazkNIf3loAiAn6lamDu1nmyw9nmdcn4MkLXzbA57PLt25MEWz7KiQ2A==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":63150},"main":"index.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"_npmUser":{"name":"ty4spd","actor":{"name":"ty4spd","type":"user","email":"cblzlz@hotmail.com"},"email":"cblzlz@hotmail.com"},"_npmVersion":"8.19.4","description":"Logger from bandeng developer team.","directories":{},"_nodeVersion":"16.20.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/bandeng-logger_1.0.8_1751533242170_0.191684116303531","host":"s3://npm-registry-packages-npm-production"}},"1.0.9":{"name":"bandeng-logger","version":"1.0.9","author":{"name":"lizhi"},"license":"ISC","_id":"bandeng-logger@1.0.9","maintainers":[{"name":"ty4spd","email":"cblzlz@hotmail.com"}],"dist":{"shasum":"008bf4c48902c4c2e9cccbe4ccd60c7470448f61","tarball":"https://registry.npmjs.org/bandeng-logger/-/bandeng-logger-1.0.9.tgz","fileCount":3,"integrity":"sha512-YdpMVN1pRibhVMLLeolDk2D7WYUO9thaOUdSeEDoKXgNvz6Zxf476jjKlnVaY6Kxj3JjcfxNKU7My+Zo8fHkIQ==","signatures":[{"sig":"MEUCIQCi3XHF+bsLW9TA0liut+PdUF/QmabwrYllknaZsjY9gQIgZ/eEzXcl0KpSHLXT5p/3NO8xFzW/AnLrCIPr1L55R/A=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":67820},"main":"index.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"_npmUser":{"name":"ty4spd","email":"cblzlz@hotmail.com"},"_npmVersion":"8.19.4","description":"Logger from bandeng developer team.","directories":{},"_nodeVersion":"16.20.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/bandeng-logger_1.0.9_1754637947382_0.23754977923038822","host":"s3://npm-registry-packages-npm-production"}},"1.0.10":{"name":"bandeng-logger","version":"1.0.10","author":{"name":"lizhi"},"license":"ISC","_id":"bandeng-logger@1.0.10","maintainers":[{"name":"ty4spd","email":"cblzlz@hotmail.com"}],"dist":{"shasum":"669f1a2ff1369c0b564ac702bfd58c1de69c9b3b","tarball":"https://registry.npmjs.org/bandeng-logger/-/bandeng-logger-1.0.10.tgz","fileCount":3,"integrity":"sha512-GvTbpgyqlbgzuQjssyC+y3sTbpznk3gJz0I/zLVxRAqHRBtA+tQfSBzIoXGUVEZy9FdO0nUcZdwuk6XX7b+bGQ==","signatures":[{"sig":"MEYCIQCaehhhv/ATE0WszNHaflj5ewZBT4GhsPfPq8CzCyrDigIhAIFGqx66mX+ikyfQLN9K9BdZ2+QrqVx0GAmdaZDBMTEs","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":107075},"main":"index.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"_npmUser":{"name":"ty4spd","email":"cblzlz@hotmail.com"},"_npmVersion":"8.19.4","description":"Logger from bandeng developer team.","directories":{},"_nodeVersion":"16.20.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/bandeng-logger_1.0.10_1756892678716_0.4540045524016041","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"bandeng-logger","version":"1.1.0","author":{"name":"lizhi"},"license":"ISC","_id":"bandeng-logger@1.1.0","maintainers":[{"name":"ty4spd","email":"cblzlz@hotmail.com"}],"dist":{"shasum":"7bad293b95169ff1027a2441f0b02dceee0d5f9c","tarball":"https://registry.npmjs.org/bandeng-logger/-/bandeng-logger-1.1.0.tgz","fileCount":3,"integrity":"sha512-S+OXHXzWmHJaI3eT34gOXOmoZBMICznjgWoZ0fJlBdB/wdFkT5DC6JCs3b6XdGpGUoZPU4K3ytYszu4C+3ZJ4Q==","signatures":[{"sig":"MEYCIQC+ynBMqxp+7iFJIJOX+Qx8DfZNysy5u4qXCs3L3Vi2sAIhAI+RmAOqkkmmjMVwZn9dzMQUQQPib8RnCfJEw6DSCf+F","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":128859},"main":"index.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"_npmUser":{"name":"ty4spd","email":"cblzlz@hotmail.com"},"_npmVersion":"8.19.4","description":"Logger from bandeng developer team.","directories":{},"_nodeVersion":"16.20.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/bandeng-logger_1.1.0_1757408681930_0.5835354892815534","host":"s3://npm-registry-packages-npm-production"}},"1.1.2":{"name":"bandeng-logger","version":"1.1.2","author":{"name":"lizhi"},"license":"ISC","_id":"bandeng-logger@1.1.2","maintainers":[{"name":"ty4spd","email":"cblzlz@hotmail.com"}],"dist":{"shasum":"f28959ecb5db26a0407373a914ca19e4ab4bc5da","tarball":"https://registry.npmjs.org/bandeng-logger/-/bandeng-logger-1.1.2.tgz","fileCount":3,"integrity":"sha512-TJg3xKGa3o3CrWZcg84GTpgNK+7njlN4A7MlNvk4urXt4cErXI5MBporLMUWEK2aVxwuKBN2naiSO9OqfHQsVg==","signatures":[{"sig":"MEQCIDzSb2ewcOlcYFVgxMGjkZezZXS9tV71eurFK5LtLsvgAiBGikH+k6pcVJdBZ9oUFHKh2Jp7/RSP760uQe01Mt+SGA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":163775},"main":"index.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"_npmUser":{"name":"ty4spd","email":"cblzlz@hotmail.com"},"_npmVersion":"8.19.4","description":"Logger from bandeng developer team.","directories":{},"_nodeVersion":"16.20.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/bandeng-logger_1.1.2_1757643916400_0.6837400029470877","host":"s3://npm-registry-packages-npm-production"}},"1.1.3":{"name":"bandeng-logger","version":"1.1.3","author":{"name":"lizhi"},"license":"ISC","_id":"bandeng-logger@1.1.3","maintainers":[{"name":"ty4spd","email":"cblzlz@hotmail.com"}],"dist":{"shasum":"f5da664bc39818bec3e29b1bcd6ba3be99d346e0","tarball":"https://registry.npmjs.org/bandeng-logger/-/bandeng-logger-1.1.3.tgz","fileCount":3,"integrity":"sha512-l04122RZaC+07+SEH0csmvouTfpO3XLN3audJerlzg+88OX2c4WLd2pVbhFrD0Hmb02cjCqdqvOVP6m04+nRpA==","signatures":[{"sig":"MEYCIQCV5NRwpXE19pUUQ4f2tVvFp1DN4jJUSBLc/VDV+EB4IAIhAKaSyIcNeBB0fEQuxvLGT0dDCrbUolFcGwNZ7uYbA1J0","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":181353},"main":"index.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"_npmUser":{"name":"ty4spd","email":"cblzlz@hotmail.com"},"_npmVersion":"8.19.4","description":"Logger from bandeng developer team.","directories":{},"_nodeVersion":"16.20.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/bandeng-logger_1.1.3_1757667791306_0.9197197173649436","host":"s3://npm-registry-packages-npm-production"}},"1.2.4":{"name":"bandeng-logger","version":"1.2.4","keywords":["logger","logging","log","bandeng","performance","monitoring","buffered","rotation","compression","json","text","utc","timezone","pm2","multi-process"],"author":{"name":"lizhi"},"license":"ISC","_id":"bandeng-logger@1.2.4","maintainers":[{"name":"ty4spd","email":"cblzlz@hotmail.com"}],"dist":{"shasum":"59f359df8e47a31ff7026078249cfb0e3abbcb72","tarball":"https://registry.npmjs.org/bandeng-logger/-/bandeng-logger-1.2.4.tgz","fileCount":10,"integrity":"sha512-za9/hmo6WrsET4z19k2oxiaVYthbcyDUtdzmat2uG/INngvQIPVbM3ms3QV5nUG5pVQAoAgWhDtGzmWCMUJ09Q==","signatures":[{"sig":"MEUCIEnp7GQot07lJ/GqPllyeQZ2v4ayQywJKyhEVIFKqJdUAiEA+iSWf79/U8rnnNbjou81o5oQwb4XT/8Qexkm4D/kwGg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":248488},"main":"index.js","engines":{"node":">=12.0.0"},"_npmUser":{"name":"ty4spd","email":"cblzlz@hotmail.com"},"_npmVersion":"8.19.4","description":"Logger from bandeng developer team.","directories":{},"_nodeVersion":"16.20.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/bandeng-logger_1.2.4_1758540635025_0.7528878263597469","host":"s3://npm-registry-packages-npm-production"}},"1.2.5":{"name":"bandeng-logger","version":"1.2.5","keywords":["logger","logging","log","bandeng","performance","monitoring","buffered","rotation","compression","json","text","utc","timezone","pm2","multi-process"],"author":{"name":"lizhi"},"license":"ISC","_id":"bandeng-logger@1.2.5","maintainers":[{"name":"ty4spd","email":"cblzlz@hotmail.com"}],"dist":{"shasum":"d984bc0877679e4a51ae8ddd54d2e58733a28566","tarball":"https://registry.npmjs.org/bandeng-logger/-/bandeng-logger-1.2.5.tgz","fileCount":10,"integrity":"sha512-HVYhrcpPlaAxoEnktL//r9XriQ4qWOZ6XbGPzYM1uzHkzSgXyl4RIO2JHrCBHMq1EV/8kDc21c3TPkQkAF+1AA==","signatures":[{"sig":"MEUCIAzh21KlEoUUB3S8ZAJR5l3qOoMVnisj8j0I+tPSPlNqAiEA2BKLAxr0B+zroDd3uQ5WwjrFqQhOw6twqlOvhH+PMkI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":250485},"main":"index.js","engines":{"node":">=12.0.0"},"_npmUser":{"name":"ty4spd","email":"cblzlz@hotmail.com"},"_npmVersion":"8.19.4","description":"Logger from bandeng developer team.","directories":{},"_nodeVersion":"16.20.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/bandeng-logger_1.2.5_1758696014190_0.893460396979888","host":"s3://npm-registry-packages-npm-production"}},"1.3.0":{"name":"bandeng-logger","version":"1.3.0","keywords":["logger","logging","log","bandeng","performance","monitoring","buffered","rotation","compression","json","text","utc","timezone","pm2","multi-process"],"author":{"name":"lizhi"},"license":"ISC","_id":"bandeng-logger@1.3.0","maintainers":[{"name":"ty4spd","email":"cblzlz@hotmail.com"}],"dist":{"shasum":"76c9702e82323b2c219600864154b49dd2448805","tarball":"https://registry.npmjs.org/bandeng-logger/-/bandeng-logger-1.3.0.tgz","fileCount":9,"integrity":"sha512-jZBSz51WqFQ5P0Tyozr1TMh6X0klu4thuPilxP/lDHnjzSl3dVfAu2owxVlGvG/tR2XzcGYRC4y4nxuhqsPsVw==","signatures":[{"sig":"MEUCIQDyqVn7kSkcEaOcBs2V1qESVFBBVxtRHqDb+Vn6mg9i9AIgNwLC90X46QTyohZDaSF/by5gxgjweDWfnsjJ6Nq/S/w=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":269931},"main":"index.js","engines":{"node":">=12.0.0"},"_npmUser":{"name":"ty4spd","email":"cblzlz@hotmail.com"},"_npmVersion":"8.19.4","description":"Logger from bandeng developer team.","directories":{},"_nodeVersion":"16.20.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/bandeng-logger_1.3.0_1758763676633_0.28290616648569555","host":"s3://npm-registry-packages-npm-production"}},"1.3.3":{"name":"bandeng-logger","version":"1.3.3","keywords":["logger","logging","log","bandeng","performance","monitoring","buffered","rotation","compression","json","text","utc","timezone","pm2","multi-process","typescript","types"],"author":{"name":"lizhi"},"license":"ISC","_id":"bandeng-logger@1.3.3","maintainers":[{"name":"ty4spd","email":"cblzlz@hotmail.com"}],"dist":{"shasum":"72ffba1deeff1cf17612a1ccf01809785bd97153","tarball":"https://registry.npmjs.org/bandeng-logger/-/bandeng-logger-1.3.3.tgz","fileCount":11,"integrity":"sha512-AZat8sOOnX8bWZKyov3aXqds1hfT1RK7xFf1Y4adjVOVo7sznbLa/j4U9nhCkUZLZW+j6b5BG4tdDMI0MRiytA==","signatures":[{"sig":"MEQCIHUfHjKcxJaiQMty/HQ/S3aM/lRrf9O/+GZAsiMmjtEzAiBw2ixnyIDXRyAOkqBjMfNnqfFxDSKDHNozJs5FWN8H3w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":292649},"main":"index.js","types":"index.d.ts","engines":{"node":">=12.0.0"},"_npmUser":{"name":"ty4spd","email":"cblzlz@hotmail.com"},"_npmVersion":"8.19.4","description":"Logger from bandeng developer team.","directories":{},"_nodeVersion":"16.20.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/bandeng-logger_1.3.3_1758854897217_0.7214823985424095","host":"s3://npm-registry-packages-npm-production"}},"1.3.4":{"name":"bandeng-logger","version":"1.3.4","keywords":["logger","logging","log","bandeng","performance","monitoring","buffered","rotation","compression","json","text","utc","timezone","pm2","multi-process","typescript","types"],"author":{"name":"lizhi"},"license":"ISC","_id":"bandeng-logger@1.3.4","maintainers":[{"name":"ty4spd","email":"cblzlz@hotmail.com"}],"dist":{"shasum":"f0dac221336c7eae3aef9373b3ca420ad198db53","tarball":"https://registry.npmjs.org/bandeng-logger/-/bandeng-logger-1.3.4.tgz","fileCount":11,"integrity":"sha512-kNULN+QJDUJDIybwEJxWl84bzwaUCOIj2oeArZbEQO/9uusMnDFob8TeWdB39aun6e8tr1KucOhAE1vaA9W1bA==","signatures":[{"sig":"MEUCIQC1xN2UBD0A5VCcAJViPsSaOMefvCR7o9txy5lV2R/UoQIgdyL3AZ4OYeymCR1OJRAJ0j/iVHIZVH6OyMgng0l6cNM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":300609},"main":"index.js","types":"index.d.ts","engines":{"node":">=12.0.0"},"_npmUser":{"name":"ty4spd","email":"cblzlz@hotmail.com"},"_npmVersion":"8.19.4","description":"Logger from bandeng developer team.","directories":{},"_nodeVersion":"16.20.2","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/bandeng-logger_1.3.4_1759978388941_0.6325826282531111","host":"s3://npm-registry-packages-npm-production"}},"1.3.6":{"name":"bandeng-logger","version":"1.3.6","keywords":["logger","logging","log","bandeng","performance","monitoring","buffered","rotation","compression","json","text","utc","timezone","pm2","multi-process","typescript","types"],"author":{"name":"lizhi"},"license":"ISC","_id":"bandeng-logger@1.3.6","maintainers":[{"name":"ty4spd","email":"cblzlz@hotmail.com"}],"dist":{"shasum":"8068b3edaf24252155dcf7b674c9e21e74cbd41e","tarball":"https://registry.npmjs.org/bandeng-logger/-/bandeng-logger-1.3.6.tgz","fileCount":11,"integrity":"sha512-BwonAhWAfZpfXQTVWfc5+VHvnQkl79UTBAYTqLgDXmm7tMduY7mm8YuJfPkgjj1Fe8dyynf9ZXKu3BHn22rmlw==","signatures":[{"sig":"MEUCIQClvFM/WGu+i2jDO+RO7KjH4+e3be7wQdG98ETP7kGvcQIgQk0w9pVAFtugdl0380nZVV6nrL/63MW5FPRrBCfL8MM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":302891},"main":"index.js","types":"index.d.ts","engines":{"node":">=12.0.0"},"_npmUser":{"name":"ty4spd","email":"cblzlz@hotmail.com"},"_npmVersion":"8.19.4","description":"Logger from bandeng developer team.","directories":{},"_nodeVersion":"16.20.2","_hasShrinkwrap":false,"devDependencies":{"jest":"30.2.0"},"_npmOperationalInternal":{"tmp":"tmp/bandeng-logger_1.3.6_1760773457700_0.7819600777218081","host":"s3://npm-registry-packages-npm-production"}},"1.3.7":{"name":"bandeng-logger","version":"1.3.7","keywords":["logger","logging","log","bandeng","performance","monitoring","buffered","rotation","compression","json","text","utc","timezone","pm2","multi-process","typescript","types"],"author":{"name":"lizhi"},"license":"ISC","_id":"bandeng-logger@1.3.7","maintainers":[{"name":"ty4spd","email":"cblzlz@hotmail.com"}],"dist":{"shasum":"9512d92e5622fae153226d4d912289e0121cf5e0","tarball":"https://registry.npmjs.org/bandeng-logger/-/bandeng-logger-1.3.7.tgz","fileCount":11,"integrity":"sha512-7+NP84zzSWuVGFoXJq6N/Ll0RpbW4k6AmmtWsP7dyMIi6b42ncouue3gq9vHJa30+QoNDaL1a8PjgImawGxjcA==","signatures":[{"sig":"MEYCIQDiXSMMY99q+WVCWhLukQ7Ie73fNlEl3aQ5PwF1q5BQyQIhAOnfD/CdLA5ebQzEqvb0FFcPtyRJ0PGD58GMEDWGAHbv","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":301893},"main":"index.js","types":"index.d.ts","engines":{"node":">=12.0.0"},"scripts":{"test":"jest"},"_npmUser":{"name":"ty4spd","email":"cblzlz@hotmail.com"},"_npmVersion":"8.19.4","description":"Logger from bandeng developer team.","directories":{},"_nodeVersion":"16.20.2","_hasShrinkwrap":false,"devDependencies":{"jest":"30.2.0"},"_npmOperationalInternal":{"tmp":"tmp/bandeng-logger_1.3.7_1761981761907_0.0006813012157629306","host":"s3://npm-registry-packages-npm-production"}},"1.3.8":{"name":"bandeng-logger","version":"1.3.8","keywords":["logger","logging","log","bandeng","performance","monitoring","buffered","rotation","compression","json","text","utc","timezone","pm2","multi-process","typescript","types"],"author":{"name":"lizhi"},"license":"ISC","_id":"bandeng-logger@1.3.8","maintainers":[{"name":"ty4spd","email":"cblzlz@hotmail.com"}],"dist":{"shasum":"f3cd7bf5b4758ad84e911a14e182618badacc075","tarball":"https://registry.npmjs.org/bandeng-logger/-/bandeng-logger-1.3.8.tgz","fileCount":11,"integrity":"sha512-uMum5NysN0jztgiKXTyMXZ5pCxpmIV8kCsSY/igKgaHkdbAShim6WEx/aApbLQDsdl10BtF2aqFjOc1ADy2G7A==","signatures":[{"sig":"MEQCIBFyQ0yyBViUHPrkppBIRrSMQJM//nlTVWASpn4dnQu/AiBQpbWDUuFOcN1so8IpnUqNWRqrdXEmy3QIL8/DeRlqpQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":317057},"main":"index.js","types":"index.d.ts","engines":{"node":">=12.0.0"},"scripts":{"test":"jest"},"_npmUser":{"name":"ty4spd","email":"cblzlz@hotmail.com"},"_npmVersion":"8.19.4","description":"Logger from bandeng developer team.","directories":{},"_nodeVersion":"16.20.2","_hasShrinkwrap":false,"devDependencies":{"jest":"30.2.0"},"_npmOperationalInternal":{"tmp":"tmp/bandeng-logger_1.3.8_1765782695746_0.3647610276898443","host":"s3://npm-registry-packages-npm-production"}},"1.3.9":{"name":"bandeng-logger","version":"1.3.9","keywords":["logger","logging","log","bandeng","performance","monitoring","buffered","rotation","compression","json","text","utc","timezone","pm2","multi-process","typescript","types"],"author":{"name":"lizhi"},"license":"ISC","_id":"bandeng-logger@1.3.9","maintainers":[{"name":"ty4spd","email":"cblzlz@hotmail.com"}],"dist":{"shasum":"f0de8ae4b8433e2c439e275fcf299eeffba6f135","tarball":"https://registry.npmjs.org/bandeng-logger/-/bandeng-logger-1.3.9.tgz","fileCount":11,"integrity":"sha512-yf4IBuVE+iqGxNPBnE07CL6nZp8Z5O2i9kjjoqHhqOkZpVEcLiv/i0m2nlofn8On6ZrSthUxAj86RRvfdz0eng==","signatures":[{"sig":"MEYCIQDEa7ACr1Mn1nl8n9ZSS3s0PIFrFr3/CcuSEgVF8NMBpAIhANWP+HEW8P+Bn+IbEjM6tjKfrz6aEnZIZ8MrX6agqaJv","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":310318},"main":"index.js","types":"index.d.ts","engines":{"node":">=12.0.0"},"scripts":{"test":"jest"},"_npmUser":{"name":"ty4spd","email":"cblzlz@hotmail.com"},"_npmVersion":"8.19.4","description":"Logger from bandeng developer team.","directories":{},"_nodeVersion":"16.20.2","_hasShrinkwrap":false,"devDependencies":{"jest":"30.2.0"},"_npmOperationalInternal":{"tmp":"tmp/bandeng-logger_1.3.9_1766662146386_0.3596724468479975","host":"s3://npm-registry-packages-npm-production"}},"1.3.10":{"name":"bandeng-logger","version":"1.3.10","keywords":["logger","logging","log","bandeng","performance","monitoring","buffered","rotation","compression","json","text","utc","timezone","pm2","multi-process","typescript","types"],"author":{"name":"lizhi"},"license":"ISC","_id":"bandeng-logger@1.3.10","maintainers":[{"name":"ty4spd","email":"cblzlz@hotmail.com"}],"dist":{"shasum":"db439befd7c93d4ebb08313a64414a071caa0541","tarball":"https://registry.npmjs.org/bandeng-logger/-/bandeng-logger-1.3.10.tgz","fileCount":11,"integrity":"sha512-0Iojn3cGfUf5zVrO9us1iAAXtJS2U7pjd5tfkY6I3p0lu+4gpl1X0Nh2o3SzdcfWrmCIZmE03CtQ7SQleDZ5qg==","signatures":[{"sig":"MEQCID9FAZKQgbYa/UJTToH3KRvUGoQdJVdIAAN9mKILQf2tAiAX5yZfGk0EyJ6zFLUNETq2gTKY19+aFyr/kJyUI4SKOA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":315083},"main":"index.js","types":"index.d.ts","engines":{"node":">=12.0.0"},"scripts":{"test":"jest"},"_npmUser":{"name":"ty4spd","email":"cblzlz@hotmail.com"},"_npmVersion":"8.19.4","description":"Logger from bandeng developer team.","directories":{},"_nodeVersion":"16.20.2","_hasShrinkwrap":false,"devDependencies":{"jest":"30.2.0"},"_npmOperationalInternal":{"tmp":"tmp/bandeng-logger_1.3.10_1767496722777_0.5258658616167713","host":"s3://npm-registry-packages-npm-production"}},"1.3.11":{"name":"bandeng-logger","version":"1.3.11","keywords":["logger","logging","log","bandeng","performance","monitoring","buffered","rotation","compression","json","text","utc","timezone","pm2","multi-process","typescript","types"],"author":{"name":"lizhi"},"license":"ISC","_id":"bandeng-logger@1.3.11","maintainers":[{"name":"ty4spd","email":"cblzlz@hotmail.com"}],"dist":{"shasum":"179806d4fdb533a2a5c18df9de4182c49ac5b576","tarball":"https://registry.npmjs.org/bandeng-logger/-/bandeng-logger-1.3.11.tgz","fileCount":11,"integrity":"sha512-DnsxTFLDq3H9OxmEZ+qsdY95igHl8Gj6vGFMOV+QoLZQopDqjetcmiDNZJUe7ioidSOd4rL9145AZCrnNx/Ikw==","signatures":[{"sig":"MEUCIDQGP++++pxPMeCWlxn8cLoNdaxTAhOurfmd8eU0ezNEAiEA1pzPayTRbiNO3P+snDhb8voWbfl9H8DVQThWrzb2lRQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":317680},"main":"index.js","types":"index.d.ts","engines":{"node":">=12.0.0"},"scripts":{"test":"jest"},"_npmUser":{"name":"ty4spd","email":"cblzlz@hotmail.com"},"_npmVersion":"8.19.4","description":"Logger from bandeng developer team.","directories":{},"_nodeVersion":"16.20.2","_hasShrinkwrap":false,"devDependencies":{"jest":"30.2.0"},"_npmOperationalInternal":{"tmp":"tmp/bandeng-logger_1.3.11_1772871335307_0.8800363935111919","host":"s3://npm-registry-packages-npm-production"}},"1.4.5":{"name":"bandeng-logger","version":"1.4.5","keywords":["logger","logging","log","bandeng","performance","monitoring","buffered","rotation","compression","json","text","utc","timezone","pm2","multi-process","typescript","types"],"author":{"name":"lizhi"},"license":"ISC","_id":"bandeng-logger@1.4.5","maintainers":[{"name":"ty4spd","email":"cblzlz@hotmail.com"}],"dist":{"shasum":"33b9c6497162f674e47985ae67c5f930fc8dc2d8","tarball":"https://registry.npmjs.org/bandeng-logger/-/bandeng-logger-1.4.5.tgz","fileCount":15,"integrity":"sha512-i9ShHAuTsDzk2cJFmbi8x7tE9kv0cFJA8t0SbFMsSX41NFgK3Gl+8QhSsTg+6R9f0OGRozYhz8tW/bA7pPkwog==","signatures":[{"sig":"MEUCIQC8p4baAFpxdBgj+0uhswa2AW9K6l/MuKdRW4VPSnGImgIgTL04yPj0qw4dkXXvOe9jHo9CHRmk0n/0GRsDTI+Jfus=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":405710},"main":"index.js","types":"index.d.ts","engines":{"node":">=12.0.0"},"gitHead":"3068cf8a5e26cd23c38eb756636ccfafd0cd640d","scripts":{"test":"jest"},"_npmUser":{"name":"ty4spd","email":"cblzlz@hotmail.com"},"_npmVersion":"10.8.2","description":"Logger from bandeng developer team.","directories":{},"_nodeVersion":"20.19.6","_hasShrinkwrap":false,"devDependencies":{"jest":"30.2.0"},"_npmOperationalInternal":{"tmp":"tmp/bandeng-logger_1.4.5_1781607757577_0.7642451549136495","host":"s3://npm-registry-packages-npm-production"}},"1.4.7":{"name":"bandeng-logger","version":"1.4.7","description":"Logger from bandeng developer team.","main":"index.js","types":"index.d.ts","engines":{"node":">=12.0.0"},"keywords":["logger","logging","log","bandeng","performance","monitoring","buffered","rotation","compression","json","text","utc","timezone","pm2","multi-process","typescript","types"],"author":{"name":"lizhi"},"license":"ISC","scripts":{"test":"jest"},"jest":{"testPathIgnorePatterns":["/test/test\\.js$","/test/testreporter\\.js$"]},"devDependencies":{"jest":"30.2.0"},"_id":"bandeng-logger@1.4.7","gitHead":"028142500479a811d401d38e4299c2259db880f8","_nodeVersion":"20.19.6","_npmVersion":"10.8.2","dist":{"integrity":"sha512-8xBvg/41T8dA/ny7rMVCOJQmUoq2YuvVrNNM8Z4nex0O3pSxvRJxeccOiboOP2i5HUhba5ACeYgo/9L2TQACvg==","shasum":"337c1809fbae9ca200f26a8f98662b8fb0aa63c1","tarball":"https://registry.npmjs.org/bandeng-logger/-/bandeng-logger-1.4.7.tgz","fileCount":16,"unpackedSize":436235,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCfE3G5w2va0368KsTH/Y7G8ZaPENxRfrCQ4Xzpq+g5xQIhAIQ6/o+eMRITnMRpyNlHddqkqk5w8muVtXI0ertMpAqb"}]},"_npmUser":{"name":"ty4spd","email":"cblzlz@hotmail.com"},"directories":{},"maintainers":[{"name":"ty4spd","email":"cblzlz@hotmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/bandeng-logger_1.4.7_1782358797424_0.11224065667257466"},"_hasShrinkwrap":false}},"time":{"created":"2025-06-02T10:17:12.297Z","modified":"2026-06-25T03:39:57.677Z","1.0.0":"2025-06-02T10:17:12.490Z","1.0.1":"2025-06-03T01:45:49.545Z","1.0.2":"2025-06-11T09:14:40.670Z","1.0.3":"2025-06-12T03:11:05.836Z","1.0.4":"2025-06-12T07:49:57.642Z","1.0.5":"2025-06-14T06:32:23.219Z","1.0.6":"2025-06-24T02:11:39.561Z","1.0.7":"2025-06-25T09:34:05.208Z","1.0.8":"2025-07-03T09:00:42.351Z","1.0.9":"2025-08-08T07:25:47.565Z","1.0.10":"2025-09-03T09:44:38.921Z","1.1.0":"2025-09-09T09:04:42.111Z","1.1.1":"2025-09-12T02:07:25.289Z","1.1.2":"2025-09-12T02:25:16.595Z","1.1.3":"2025-09-12T09:03:11.491Z","1.2.0":"2025-09-20T04:26:39.224Z","1.2.1":"2025-09-20T08:32:54.316Z","1.2.2":"2025-09-20T11:29:09.807Z","1.2.3":"2025-09-22T08:50:37.579Z","1.2.4":"2025-09-22T11:30:35.206Z","1.2.5":"2025-09-24T06:40:14.379Z","1.3.0":"2025-09-25T01:27:56.824Z","1.3.1":"2025-09-26T01:39:23.156Z","1.3.2":"2025-09-26T01:55:47.055Z","1.3.3":"2025-09-26T02:48:17.390Z","1.3.4":"2025-10-09T02:53:09.199Z","1.3.5":"2025-10-18T07:07:44.551Z","1.3.6":"2025-10-18T07:44:17.968Z","1.3.7":"2025-11-01T07:22:42.124Z","1.3.8":"2025-12-15T07:11:35.905Z","1.3.9":"2025-12-25T11:29:06.540Z","1.3.10":"2026-01-04T03:18:42.935Z","1.3.11":"2026-03-07T08:15:35.451Z","1.4.0":"2026-06-13T08:01:32.426Z","1.4.1":"2026-06-16T03:23:37.904Z","1.4.2":"2026-06-16T08:59:22.572Z","1.4.3":"2026-06-16T09:54:57.376Z","1.4.4":"2026-06-16T10:41:07.362Z","1.4.5":"2026-06-16T11:02:37.696Z","1.4.6":"2026-06-25T03:01:41.002Z","1.4.7":"2026-06-25T03:39:57.573Z"},"author":{"name":"lizhi"},"license":"ISC","keywords":["logger","logging","log","bandeng","performance","monitoring","buffered","rotation","compression","json","text","utc","timezone","pm2","multi-process","typescript","types"],"description":"Logger from bandeng developer team.","maintainers":[{"name":"ty4spd","email":"cblzlz@hotmail.com"}],"readme":"# Bandeng Logger\r\n\r\n[English](#English) | [中文](#中文)\r\n\r\n---\r\n\r\n## English\r\n\r\nA powerful logging library from Bandeng developer team.\r\n\r\n### 🚀 **Version 1.4.7 updates** (2026-06-25)\r\n\r\n1. **OpenObserve ingest payload normalization** (`lib/openobserve_payload.js`, `reporter.normalizePayload`, default `true`): HTTP reporter extracts configured fields (default **`rid`**) to the top level and puts the **full second argument** in **`rawData`**. Local file and console JSON are unchanged. Dual-arg `logger.info('tag', {rid, ...})` needs no application changes.\r\n2. **`reporter.extractTopLevelFields`** (default `['rid']`, max **10**): Promote configured business fields from the second argument to ingest top level; full payload always remains in `rawData`.\r\n3. **`reporter.rawDataAsString`** (default `false`): By default `rawData` is a readable **object-literal string** (unquoted keys, single-quoted strings — not JSON). Set `true` to use `JSON.stringify` instead. Either form is a single string column so OpenObserve does not flatten nested objects.\r\n4. **`rawData` reserved key**: Business object args must not pass `rawData` (strip + `console.warn`, same as other OpenObserve reserved keys). **`rid` remains a normal business field.**\r\n5. **`destroy()` / `updateOptions()` lifecycle**: Drains logs queued during startup rotation checks before teardown, preventing empty `server.log` on fast shutdown or `logDir` change.\r\n\r\n### Installation\r\n\r\n```bash\r\nnpm install bandeng-logger\r\n```\r\n\r\n### Usage\r\n\r\n```javascript\r\nconst Logger = require('bandeng-logger')\r\n```\r\n\r\n### Features\r\n\r\n- 📝 Multiple log levels (fatal, error, warn, info, debug, trace)\r\n- 🔄 Automatic log rotation by size and time\r\n- 🎨 Colorful console output with customizable colors\r\n- 📊 Built-in performance monitoring for slow logs (explicitly enabled)\r\n- 🎯 Force print conditions for selective debugging regardless of log level\r\n- 🔍 Multi-process support with process ID tracking and concurrent rotation safety\r\n- 📦 Log compression (gzip)\r\n- 🏷️ Child loggers for modular logging\r\n- ⚡ Zero-blocking buffered writing with synchronous semantics\r\n- 🕒 High-precision UTC timezone support with millisecond timestamps\r\n- 📈 Advanced log write performance monitoring with timeout protection (requires explicit enablement)\r\n- 📋 Support for both JSON and text log formats\r\n- 🔒 Configurable error log storage with custom level separation\r\n- 🕐 Customizable log rotation interval and buffer flush timing\r\n- 🛡️ Enhanced error handling with circular reference detection and recursion prevention\r\n- 🧹 Intelligent automatic file management and concurrent cleanup\r\n- 📁 Smart log file splitting with size limit enforcement\r\n- 🔧 Comprehensive process lifecycle management with signal handling\r\n- ✅ Advanced configuration validation with boundary checking\r\n- 🚀 Atomic file operations with concurrent safety\r\n- 📊 Real-time statistics and monitoring\r\n- 🔄 Intelligent buffer management with dynamic thresholds\r\n- 🔮 Modern JavaScript data type support (BigInt, Symbol, Map, Set, TypedArray, Promise)\r\n- 🛡️ Serialization depth protection and memory leak prevention\r\n- 🔒 Context content sanitization with length limits and security filtering\r\n- ⚙️ Runtime configuration updates with hot reloading\r\n- 📈 Advanced request performance tracking with context handling\r\n- 🔧 Customizable ctx field processing with injection functions\r\n- 🛡️ Resource leak prevention with automatic stream cleanup\r\n- 🚫 Path traversal protection with filename sanitization\r\n- 🔄 Intelligent retry mechanism with exponential backoff\r\n- 📊 Real-time memory usage monitoring and leak prevention\r\n- 🎯 Unified error handling strategy across all operations\r\n- 🌐 HTTP request logging with framework auto-detection\r\n- 🧠 Smart IP extraction supporting multiple proxy headers\r\n- 📡 OpenObserve HTTP JSON reporter with batch upload, retry, and circuit breaker (v1.4.0+); ingest payload normalization — `rid` + `rawData` (v1.4.7+)\r\n- 📘 Full TypeScript support with complete type definitions\r\n\r\n### Quick Start\r\n\r\n#### Basic Usage\r\n\r\n```javascript\r\nconst Logger = require('bandeng-logger')\r\nconst logger = new Logger({logLevel: 'info', logFormat: 'text'})\r\n\r\nlogger.info('Info log')\r\nlogger.error('Error log')\r\nlogger.warn('Warning log')\r\n\r\nconst moduleLogger = logger.createChildLogger('UserModule')\r\nmoduleLogger.info('User login success')\r\n```\r\n\r\n#### TypeScript Usage\r\n\r\n```typescript\r\n// CommonJS import (recommended for existing projects)\r\n// Note: When package is published, use: const Logger = require('bandeng-logger')\r\nconst Logger = require('./index') // Use relative path during development\r\n\r\n// Or use ES6 module import\r\n// import Logger from 'bandeng-logger'\r\n\r\n// Type definitions (manual declaration needed with CommonJS)\r\ntype LogLevel = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace'\r\ntype LogFormat = 'json' | 'text'\r\ntype FrameworkStyle = 'koa' | 'express'\r\n\r\ninterface LoggerOptions {\r\n    logLevel?: LogLevel\r\n    logFormat?: LogFormat\r\n    /** Service name for JSON logs (v1.4.2+) */\r\n    source?: string\r\n    logDir?: string\r\n    rotate?: number\r\n    maxFiles?: number\r\n    traceLogWrite?: boolean\r\n    /** OpenObserve HTTP reporter (v1.4.0+) */\r\n    reporter?: {\r\n        enabled?: boolean\r\n        url?: string\r\n        authorization?: string\r\n        disableLocalFileWrite?: boolean\r\n        /** Normalize OpenObserve ingest payload (v1.4.7+), default true */\r\n        normalizePayload?: boolean\r\n        /** rawData: default object-literal string; true for JSON.stringify (v1.4.7+) */\r\n        rawDataAsString?: boolean\r\n        extractTopLevelFields?: string[]\r\n    }\r\n}\r\n\r\n// Complete type-safe configuration\r\nconst loggerOptions: LoggerOptions = {\r\n    logLevel: 'info' as LogLevel,\r\n    logFormat: 'json' as LogFormat,\r\n    logDir: './logs'\r\n}\r\n\r\n// Create Logger instance\r\nconst logger = new Logger(loggerOptions)\r\n\r\n// All methods have complete type hints and comments\r\nlogger.info('TypeScript info log')\r\nlogger.error('TypeScript error log', {error: 'Something went wrong'})\r\nlogger.debug('Debug with structured data', {userId: 123, action: 'login'})\r\n\r\n// Static methods also have type hints\r\nconst ctxProcessor = Logger.getCtxProcessor('whitelist', ['userId', 'action'])\r\n\r\n// Complete type safety and intelligent code completion support\r\nconst stats = logger.getStatistics()\r\nconst success = logger.updateOptions({logLevel: 'debug'})\r\n```\r\n\r\n#### Basic Configuration Examples\r\n\r\n```javascript\r\nconst Logger = require('bandeng-logger')\r\n\r\n// Minimal configuration - uses all default values\r\nconst simpleLogger = new Logger()\r\n\r\n// Or basic configuration - basic customizations\r\nconst basicLogger = new Logger({\r\n    logLevel: 'info',\r\n    logFormat: 'json'\r\n})\r\n```\r\n\r\n#### Recommended Production Configuration\r\n\r\n```javascript\r\nconst Logger = require('bandeng-logger')\r\nconst config = require('./config.js')\r\n\r\n// Create production-ready logger instance with essential customizations\r\nconst defaultLogger = new Logger({\r\n    // Essential: Dynamic log level based on environment\r\n    logLevel: config.isTest ? 'trace' : 'warn',\r\n    // Essential: Dynamic log format based on environment\r\n    logFormat: config.isTest ? 'text' : 'json',\r\n\r\n    // Custom: Adjust log rotation size if 100MB is not suitable\r\n    rotate: 20, // 20MB instead of default 100MB\r\n    // Custom: Adjust rotation check interval if 1 hour is not suitable\r\n    interval: 600, // Every 10 minutes instead of default 1 hour\r\n\r\n    // Optional: Enable performance monitoring (disabled by default)\r\n    traceLogWrite: config.enablePerformanceMonitoring || false,\r\n\r\n    // Optional: Custom color scheme for different environments\r\n    colors: {\r\n        trace: 'gray',\r\n        debug: 'blue',\r\n        info: 'white',\r\n        warn: config.isTest ? 'yellow' : 'white',\r\n        error: 'red',\r\n        fatal: 'magenta'\r\n    },\r\n\r\n    // Optional: Custom timezone (default is auto-detected)\r\n    utcFormat: 8 // UTC+8 for timestamps\r\n\r\n    // Note: HTTP request logging is enabled via method calls, no configuration needed\r\n    // Use logger.logHttpRequest() to log HTTP requests\r\n\r\n    // Note: The following use default values, no need to specify:\r\n    // logDir: './logs' (default)\r\n    // compress: 'none' (default)\r\n    // maxFiles: 15 (default)\r\n    // bufferSize: 65536 (default)\r\n    // flushInterval: 1000 (default)\r\n    // forceSyncLevels: ['fatal'] (default)\r\n    // ctxProcessor: null (default)\r\n})\r\n\r\n// Export essential logging utilities\r\nmodule.exports = {\r\n    // Core logger instance\r\n    logger: defaultLogger,\r\n\r\n    // Essential methods for application use\r\n    createChildLogger: defaultLogger.createChildLogger.bind(defaultLogger),\r\n    traceRequestPerformance: defaultLogger.traceRequestPerformance.bind(defaultLogger),\r\n\r\n    // HTTP request logging (recommended for production)\r\n    logHttpRequest: defaultLogger.logHttpRequest.bind(defaultLogger),\r\n\r\n    // Configuration management (for advanced use)\r\n    getOptions: defaultLogger.getOptions.bind(defaultLogger),\r\n    updateOptions: defaultLogger.updateOptions.bind(defaultLogger),\r\n\r\n    // Statistics and monitoring (for operational use)\r\n    getStatistics: defaultLogger.getStatistics.bind(defaultLogger),\r\n    resetStatistics: defaultLogger.resetStatistics.bind(defaultLogger)\r\n}\r\n```\r\n\r\n#### Parameter Usage Examples\r\n\r\n```javascript\r\n// ✅ Single parameter - any type supported\r\nlogger.info('Simple message')\r\nlogger.debug(42)\r\nlogger.warn({error: 'Something went wrong', code: 500})\r\n\r\n// ✅ Two parameters - first must be string, second can be any type\r\nlogger.info('User Login', {userId: 123, ip: '192.168.1.100'})\r\nlogger.debug('Database Query', 'SELECT * FROM users WHERE id = ?')\r\n\r\n// ✅ Complex logging with structured data\r\nlogger.info('Payment Processed', {\r\n    transactionId: 'txn_123456',\r\n    amount: 99.99,\r\n    currency: 'USD',\r\n    userId: 456,\r\n    timestamp: new Date(),\r\n    metadata: {\r\n        paymentMethod: 'credit_card',\r\n        gateway: 'stripe'\r\n    }\r\n})\r\n\r\n// ❌ Avoid - multiple separate parameters (extra parameters ignored)\r\nlogger.debug('Event', 'param1', 'param2', 'param3') // Only first 2 processed\r\n\r\n// ❌ Avoid - first parameter not a string (warning issued)\r\nlogger.debug(123, 'This will generate a warning')\r\n```\r\n\r\n### Parameter Passing Rules in Detail\r\n\r\n#### Basic Parameter Rules\r\n\r\n- **Maximum 2 parameters supported**, extra parameters are ignored\r\n- **1 parameter**: Supports any type, including strings, numbers, objects, arrays, etc.\r\n- **2 parameters**: First parameter must be string type, otherwise warning is issued\r\n\r\n#### How to Pass Multiple Values\r\n\r\nWhen you need to log multiple related values, wrap them in an object as the second parameter:\r\n\r\n```javascript\r\n// ✅ Recommended: Wrap multiple values in an object\r\nlogger.debug('User Login', {\r\n    userId: 123,\r\n    username: 'john_doe',\r\n    ip: '192.168.1.100',\r\n    timestamp: Date.now(),\r\n    metadata: {source: 'web'}\r\n})\r\n\r\n// ❌ Avoid: Multiple separate parameters (will be ignored)\r\nlogger.debug('User Login', 'john_doe', 123, '192.168.1.100') // Only first 2 processed\r\n\r\n// ✅ Single parameter with object\r\nlogger.debug({\r\n    event: 'user_login',\r\n    userId: 123,\r\n    username: 'john_doe',\r\n    ip: '192.168.1.100'\r\n})\r\n```\r\n\r\n#### JSON Format Object Type Special Handling\r\n\r\n**Important Note**: When using JSON format, the second parameter has special handling rules for Object types. Additionally, complex data types in the `msg` field include type information:\r\n\r\n**Expansion Rules**\r\n\r\n- **Object Expansion**: When the second parameter is a plain object (not array, not null, not undefined), all its properties are directly expanded into the log object\r\n- **Other Types Use msg Key**: Arrays, strings, numbers, booleans, null, undefined and other types are assigned to the `msg` field\r\n\r\n**Examples**\r\n\r\n```javascript\r\n// ✅ Objects are expanded - properties appear at root level\r\nlogger.info('User Login', {userId: 123, ip: '192.168.1.100'})\r\n// Output: {\"time\":\"...\", \"level\":\"INFO\", \"logTag\":\"User Login\", \"userId\":123, \"ip\":\"192.168.1.100\"}\r\n\r\n// ✅ Arrays use msg key - preserve array structure\r\nlogger.debug('Data List', [1, 2, 3, 4])\r\n// Output: {\"time\":\"...\", \"level\":\"DEBUG\", \"logTag\":\"Data List\", \"msg\":[1,2,3,4]}\r\n\r\n// ✅ Strings use msg key\r\nlogger.warn('Warning Message', 'Disk space low')\r\n// Output: {\"time\":\"...\", \"level\":\"WARN\", \"logTag\":\"Warning Message\", \"msg\":\"Disk space low\"}\r\n\r\n// ✅ Numbers use msg key\r\nlogger.error('Error Code', 500)\r\n// Output: {\"time\":\"...\", \"level\":\"ERROR\", \"logTag\":\"Error Code\", \"msg\":500}\r\n```\r\n\r\n**Design Intent**\r\n\r\nThis design enables:\r\n\r\n1. **Structured Logging**: Object expansion provides clearer field structure for log analysis and querying\r\n2. **Backward Compatibility**: Arrays and other types still use `msg` key for compatibility\r\n3. **Type Safety**: Prevents accidental property overwrites, maintaining clear log structure\r\n\r\n### Configuration Options\r\n\r\n| Option               | Type     | Default           | Description                                                                                                                                                                                                                            |\r\n| -------------------- | -------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\r\n| logLevel             | String   | 'warn'            | Log level, options: fatal, error, warn, info, debug, trace                                                                                                                                                                             |\r\n| logFormat            | String   | 'json'            | Log format, options: json, text                                                                                                                                                                                                        |\r\n| source               | String   | auto              | Service name for **JSON logs only** (serialized as top-level **`logSource`**, sibling to `pid`). If omitted, derived from PM2 `name` / `npm_package_name` (without instance suffix); otherwise `node`. See **JSON fixed fields** below |\r\n| utcFormat            | Number   | auto              | UTC timezone, range: -12 to 14. If not provided, uses server's current timezone                                                                                                                                                        |\r\n| maxLength            | Number   | 1000              | Maximum log message length, will be truncated if exceeded                                                                                                                                                                              |\r\n| logDir               | String   | './logs'          | Log directory (will be converted to absolute path automatically), will be created if not exists                                                                                                                                        |\r\n| logFileName          | String   | 'server.log'      | Main log filename, must end with .log extension                                                                                                                                                                                        |\r\n| errorLogFileName     | String   | 'error.log'       | Error log filename, must end with .log extension, only effective when separateErrorLog is true                                                                                                                                         |\r\n| separateErrorLog     | Boolean  | true              | Whether to separate error logs into a different file                                                                                                                                                                                   |\r\n| separateErrorLevels  | Array    | ['error','fatal'] | Which log levels to separate into error file, customizable                                                                                                                                                                             |\r\n| traceLogWrite        | Boolean  | false             | Enable log write performance monitoring (disabled by default, must be explicitly set to true), logs exceeding 100ms will be recorded                                                                                                   |\r\n| rotate               | Number   | 100               | Log rotation size in MB. Applies independently to the main log and, when `separateErrorLog` is true, the error log file                                                                                                                |\r\n| interval             | Number   | 3600              | Log rotation check interval (seconds), min: 60, max: 604800 (7 days)                                                                                                                                                                   |\r\n| compress             | String   | 'none'            | Log compression method, options: none, gzip                                                                                                                                                                                            |\r\n| maxFiles             | Number   | 15                | Maximum number of log files to keep, oldest files will be deleted when exceeded                                                                                                                                                        |\r\n| bufferSize           | Number   | 65536             | Buffer size in bytes (64KB), min: 1024, max: 10MB                                                                                                                                                                                      |\r\n| flushInterval        | Number   | 1000              | Buffer flush interval (ms), min: 100, max: 60000                                                                                                                                                                                       |\r\n| forceSyncLevels      | Array    | ['fatal']         | Log levels that should always be written synchronously. Configure as `['error', 'fatal']` to ensure ERROR and FATAL logs are immediately written to disk (useful for debugging and crash scenarios).                                   |\r\n| forcePrintConditions | Array    | []                | Array of condition functions for forced log printing regardless of log level, useful for business debugging                                                                                                                            |\r\n| ctxProcessor         | Function | null              | Custom ctx field processor **ONLY for traceRequestPerformance method** (designed for internal RPC communication), null uses default sanitizeContextForLogging                                                                          |\r\n| colors               | Object   | see below         | Log color configuration, supports custom colors for each level                                                                                                                                                                         |\r\n| reporter             | Object   | see below         | OpenObserve HTTP JSON reporter (v1.4.0+). Async batch upload with retry and circuit breaker. Requires `logFormat: 'json'`; disabled by default. See **OpenObserve HTTP Reporter** below for nested options                             |\r\n\r\n#### JSON fixed fields (v1.4.2+ `options.source`, v1.4.3+ `logTag`, v1.4.5+ `logSource`; `logFormat: 'json'` only)\r\n\r\nCore fields are emitted in this order: `time` → `level` → `logSource` → `process` → `pid` → `logTag` → `msg` → **other business fields**. Constructor option **`source`** maps to JSON **`logSource`**. **`logTag` / `logSource` are library-managed** — object payload must not supply `tag`/`logTag`/`source`/`logSource` (stripped with `console.warn`; see `lib/openobserve_fields.js`).\r\n\r\n| Field       | How set                     | Description                                                                                                                                                                                                                                |\r\n| ----------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\r\n| `time`      | Auto                        | Log timestamp with millisecond precision. Timezone from `options.utcFormat` (default: server local offset). Format: `YYYY-MM-DD HH:mm:ss.sss UTC±N`.                                                                                       |\r\n| `level`     | Auto                        | Uppercase level string: `FATAL`, `ERROR`, `WARN`, `INFO`, `DEBUG`, `TRACE`.                                                                                                                                                                |\r\n| `logSource` | `options.source` (optional) | Logical **service name** (no PM2 instance suffix). Default: PM2 `name` / `npm_package_name`, or `node`. Used for OpenObserve / monitoring aggregation.                                                                                     |\r\n| `process`   | Auto                        | Runtime process label: `{name}-{instanceId}` (PM2 instance suffix included when present). Distinct from `logSource`.                                                                                                                       |\r\n| `pid`       | Auto                        | Node.js `process.pid` of the writing process.                                                                                                                                                                                              |\r\n| `logTag`    | Auto / call args            | Two-arg: first string arg. Single-arg JSON: `[eventName]` inside `process.on` handlers (e.g. `[SIGINT]`), else `[logger] no tag`. **Not** taken from object payload (`tag`/`logTag` keys stripped).                                        |\r\n| `msg`       | Auto / call args            | Primary message body when the payload is not fully expanded as root fields: second arg (non-plain-object), string/number/boolean/array, or `msg` key on object. Plain object single-arg logs expand properties to root and may omit `msg`. |\r\n| _(others)_  | Expanded object fields      | Additional keys from a plain-object argument (e.g. `userId`, `stack`) appear **after** `msg` in serialized order.                                                                                                                          |\r\n\r\n```javascript\r\nconst logger = new Logger({\r\n    logFormat: 'json',\r\n    source: 'api_server', // optional\r\n    utcFormat: 8 // optional; affects `time` field\r\n})\r\n```\r\n\r\n#### Color Configuration\r\n\r\n| Level | Default Color | Description                    |\r\n| ----- | ------------- | ------------------------------ |\r\n| fatal | brightRed     | Fatal errors, using bright red |\r\n| error | red           | Errors, using red              |\r\n| warn  | brightYellow  | Warnings, using bright yellow  |\r\n| info  | gray          | Information, using gray        |\r\n| debug | blue          | Debug, using blue              |\r\n| trace | white         | Trace, using white             |\r\n\r\n**Available Colors:** black, red, green, yellow, blue, magenta, cyan, white, gray, brightBlack, brightRed, brightGreen, brightYellow, brightBlue, brightMagenta, brightCyan, brightWhite\r\n\r\n#### OpenObserve HTTP Reporter (v1.4.0+)\r\n\r\nNested options for the top-level **`reporter`** configuration object. Asynchronously ships JSON logs to [OpenObserve](https://openobserve.ai/) via native `http`/`https` (Node 12+). Disabled by default; does not block local file or console output.\r\n\r\n**Requirements:** `logFormat: 'json'` and `reporter.enabled: true`. Text format logs are not reported.\r\n\r\n| Option                                   | Type     | Default   | Description                                                                                                                                                                                                                                       |\r\n| ---------------------------------------- | -------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\r\n| reporter.enabled                         | Boolean  | false     | Enable HTTP reporter                                                                                                                                                                                                                              |\r\n| reporter.url                             | String   | —         | OpenObserve ingest URL (required when enabled)                                                                                                                                                                                                    |\r\n| reporter.authorization                   | String   | —         | Full `Authorization` header value, e.g. `Basic xxx` (required when enabled)                                                                                                                                                                       |\r\n| reporter.disableLocalFileWrite           | Boolean  | false     | When enabled together with reporter, skip local file writes (console + HTTP only)                                                                                                                                                                 |\r\n| reporter.timeout                         | Number   | 10000     | Single HTTP request timeout (ms)                                                                                                                                                                                                                  |\r\n| reporter.retry.enabled                   | Boolean  | false     | Retry on timeout / network / 5xx errors                                                                                                                                                                                                           |\r\n| reporter.retry.maxRetries                | Number   | 2         | Max extra retries (excluding first attempt)                                                                                                                                                                                                       |\r\n| reporter.retry.baseDelay                 | Number   | 500       | Base backoff delay (ms), exponential                                                                                                                                                                                                              |\r\n| reporter.batch.maxSize                   | Number   | 50        | Max logs per batch POST                                                                                                                                                                                                                           |\r\n| reporter.batch.flushInterval             | Number   | 1000      | Periodic flush interval (ms)                                                                                                                                                                                                                      |\r\n| reporter.batch.maxQueueSize              | Number   | 2000      | Queue cap; oldest entries dropped when full                                                                                                                                                                                                       |\r\n| reporter.batch.maxInFlight               | Number   | 2         | Max concurrent HTTP requests                                                                                                                                                                                                                      |\r\n| reporter.circuitBreaker.failureThreshold | Number   | 5         | Consecutive failures before circuit opens                                                                                                                                                                                                         |\r\n| reporter.circuitBreaker.recoveryTimeout  | Number   | 60000     | Circuit recovery wait (ms)                                                                                                                                                                                                                        |\r\n| reporter.normalizePayload                | Boolean  | true      | **(v1.4.7+)** Before HTTP POST, normalize ingest shape: top-level fields per `extractTopLevelFields` + `rawData` (see below). Local/console JSON unchanged. Set `false` to send formatter output as-is.                                           |\r\n| reporter.extractTopLevelFields           | String[] | `['rid']` | **(v1.4.7+)** Field names to promote from the second argument to ingest top level (max **10**). Cannot include library fields (`time`, `level`, `logSource`, `logTag`, `process`, `pid`, `rawData`, …). Full payload always remains in `rawData`. |\r\n| reporter.rawDataAsString                 | Boolean  | false     | **(v1.4.7+)** `false` (default): `rawData` is an object-literal **string** (readable, not JSON). `true`: `JSON.stringify`. Both avoid OpenObserve flattening nested objects to `rawData_*` columns.                                               |\r\n\r\n```javascript\r\nconst logger = new Logger({\r\n    logLevel: 'info',\r\n    logFormat: 'json',\r\n    reporter: {\r\n        enabled: true,\r\n        url: 'http://host:5080/api/default/nodejs/_json',\r\n        authorization: 'Basic <access-key>',\r\n        timeout: 10000,\r\n        disableLocalFileWrite: false, // set true for remote-only logging\r\n        normalizePayload: true, // default; OpenObserve ingest only\r\n        rawDataAsString: false, // default: object-literal string; true for JSON.stringify\r\n        extractTopLevelFields: ['rid', 'path'], // max 10 fields\r\n        retry: {enabled: true, maxRetries: 2},\r\n        batch: {maxSize: 50, flushInterval: 1000, maxQueueSize: 2000}\r\n    }\r\n})\r\n\r\nlogger.info('user.login', {rid: '195629686594408451', userId: 123, ip: '192.168.1.100'})\r\n```\r\n\r\n**OpenObserve ingest payload (v1.4.7+, `normalizePayload: true` default)**\r\n\r\nDual-arg convention: first argument → `logTag`, second argument → business payload. **File and console output keep the formatter JSON** (business fields may be flattened at top level). **HTTP reporter only** rewrites the POST object to:\r\n\r\n| Ingest field                                             | Description                                                                                                                                                                                                                                                              |\r\n| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\r\n| `time`, `level`, `logSource`, `process`, `pid`, `logTag` | Same as local JSON                                                                                                                                                                                                                                                       |\r\n| `rid`                                                    | Extracted from the second argument when present (cross-service trace correlation)                                                                                                                                                                                        |\r\n| _(custom)_                                               | Fields listed in `reporter.extractTopLevelFields` (default `['rid']`, max **10**)                                                                                                                                                                                        |\r\n| `msg`                                                    | Present only when the payload contains a **non-object** `msg` key **and** `msg` is not in `extractTopLevelFields`                                                                                                                                                        |\r\n| `rawData`                                                | **Full second argument** as an object-literal **string** by default (`rawDataAsString: false`); keys unquoted, strings single-quoted (not JSON). Set `rawDataAsString: true` for `JSON.stringify`. Always one string column — OpenObserve will not keep a nested object. |\r\n\r\nExample — application code:\r\n\r\n```javascript\r\nlogger.error('Middleware.Overall.Dynamic.checkId', {\r\n    rid: '195629686594408451',\r\n    path: '/',\r\n    error: {code: 3004, msg: 'Invalid params', data: 'Invalid ID'},\r\n    msg: 'catch error'\r\n})\r\n```\r\n\r\nOpenObserve receives (simplified):\r\n\r\n```javascript\r\n// POST body is a JSON array; one ingest object (rawData is a string column):\r\n{\r\n    time: '2026-06-24 20:02:31.907 UTC+8',\r\n    level: 'ERROR',\r\n    logSource: 'api_server',\r\n    process: 'api_server-17',\r\n    pid: 2090967,\r\n    logTag: 'Middleware.Overall.Dynamic.checkId',\r\n    rid: '195629686594408451',\r\n    msg: 'catch error',\r\n    rawData: \"{rid: '195629686594408451', path: '/', error: {code: 3004, msg: 'Invalid params', data: 'Invalid ID'}, msg: 'catch error'}\"\r\n}\r\n```\r\n\r\n**Notes:**\r\n\r\n- POST body is a JSON **array** of log objects (normalized when `normalizePayload: true`).\r\n- Business object args must not include `rawData` (library-managed on ingest; stripped with `console.warn` like other reserved keys).\r\n- **`rid` is not a reserved business key** — pass it in the second argument as usual.\r\n- Reporter failures are logged via `console.warn('[logger-reporter] ...')` only — never through the logger itself (prevents recursion).\r\n- On `destroy()`, remaining queued logs are flushed before shutdown (including logs queued during startup rotation checks).\r\n- **OpenObserve query tip:** Configure `ZO_FEATURE_QUICK_MODE_FIELDS` with `rid`, `rawData`, `logtag`, `time`, `process`, `pid`, `msg`. Default object-literal `rawData` is human-readable in the UI; set `rawDataAsString: true` if you need `JSON.parse` in downstream tooling.\r\n\r\n### Log Levels\r\n\r\n```javascript\r\nlogger.fatal('Fatal error')\r\nlogger.error('Error')\r\nlogger.warn('Warning')\r\nlogger.info('Info')\r\nlogger.debug('Debug')\r\nlogger.trace('Trace')\r\n```\r\n\r\n**Note**: Business error objects (e.g., `{code: 2005, msg: '', data: ''}`) are fully supported. All properties are correctly serialized and logged, even when values are empty strings or null.\r\n\r\n### Advanced Features\r\n\r\n#### Modern JavaScript Data Types Support\r\n\r\nLogger fully supports modern JavaScript data types with intelligent serialization:\r\n\r\n```javascript\r\n// BigInt support\r\nlogger.debug('BigInt value:', 123n) // Output: \"123n\"\r\n\r\n// Symbol support\r\nlogger.debug('Symbol value:', Symbol('test')) // Output: \"[Symbol: test]\"\r\n\r\n// Map and Set support\r\nconst myMap = new Map([\r\n    ['key1', 'value1'],\r\n    ['key2', 'value2']\r\n])\r\nconst mySet = new Set([1, 2, 3, 'test'])\r\n\r\nlogger.debug('Map object:', myMap) // Output: {\"[Map]\": {\"key1\": \"value1\", \"key2\": \"value2\"}}\r\nlogger.debug('Set object:', mySet) // Output: {\"[Set]\": [1, 2, 3, \"test\"]}\r\n\r\n// TypedArray support\r\nconst typedArray = new Uint8Array([1, 2, 3, 4, 5])\r\nlogger.debug('TypedArray:', typedArray) // Output: {\"[TypedArray]\": \"Uint8Array\", \"length\": 5}\r\n\r\n// Promise support\r\nconst promise = Promise.resolve('test')\r\nlogger.debug('Promise:', promise) // Output: \"[Promise]\"\r\n```\r\n\r\n#### Performance Monitoring\r\n\r\nPerformance monitoring is **disabled by default** and must be explicitly enabled:\r\n\r\n```javascript\r\n// Enable performance monitoring\r\nconst logger = new Logger({\r\n    traceLogWrite: true // Explicitly enable performance monitoring\r\n})\r\n\r\n// Now performance monitoring is active for all log operations\r\n// Note: Performance monitoring uses a default threshold of 100ms for log write operations\r\nlogger.debug('This log write will be monitored for performance')\r\n```\r\n\r\n```javascript\r\n// Recommended approach - advanced request performance tracking for internal RPC communication\r\n// This method is specifically designed for tracking performance between internal services\r\nlogger.traceRequestPerformance(\r\n    {\r\n        source: 'client-1',\r\n        path: '/api/users',\r\n        userId: 123,\r\n        method: 'GET',\r\n        ip: '192.168.1.100'\r\n    },\r\n    150,\r\n    100\r\n) // 150ms latency, 100ms threshold\r\n\r\n// Force print all requests regardless of log level (v1.3.4+)\r\nlogger.traceRequestPerformance(\r\n    {\r\n        source: 'client-1',\r\n        path: '/api/users',\r\n        userId: 123,\r\n        method: 'GET'\r\n    },\r\n    50, // 50ms latency (below threshold)\r\n    100, // 100ms threshold\r\n    true // Force print: true = ignore log level, false = follow log level\r\n)\r\n\r\n// Enable force print for debugging all requests\r\nlogger.traceRequestPerformance(ctx, latency, threshold, true)\r\n\r\n// Custom tag for filtering performance logs (v1.3.5+)\r\nlogger.traceRequestPerformance(\r\n    {\r\n        source: 'client-1',\r\n        path: '/api/users',\r\n        userId: 123\r\n    },\r\n    150,\r\n    100,\r\n    false, // forcePrint\r\n    'api-performance' // custom tag\r\n)\r\n// Logs will include {\"logTag\":\"api-performance\"} for easy filtering\r\n```\r\n\r\n#### Context Content Sanitization\r\n\r\nLogger automatically sanitizes context content for security and readability:\r\n\r\n```javascript\r\n// Context content is automatically sanitized\r\nlogger.traceRequestPerformance(\r\n    {\r\n        userId: 123,\r\n        requestData: 'A'.repeat(2000), // Long string will be truncated\r\n        largeArray: Array(100).fill('item'), // Large array will be truncated\r\n        sensitiveData: () => 'secret', // Functions will be filtered\r\n        buffer: Buffer.from('data'), // Buffers will be filtered\r\n        nested: {\r\n            veryDeep: 'B'.repeat(1500), // Nested long strings also truncated\r\n            complex: {\r\n                data: new Map([['key', 'value']]) // Complex objects handled\r\n            }\r\n        }\r\n    },\r\n    150,\r\n    100\r\n)\r\n\r\n// Output:\r\n// - Strings longer than 1000 chars are truncated with \"...[truncated X chars]\"\r\n// - Arrays larger than 50 items are truncated with \"...[truncated X items]\"\r\n// - Functions and Buffers are replaced with \"[function]\" and \"[Buffer]\"\r\n// - Complex objects are safely serialized\r\n```\r\n\r\n#### Automatic Error Logging\r\n\r\nLogger automatically handles `uncaughtException` and `unhandledRejection` events, so applications using this package **do not need to manually listen and handle these errors**.\r\n\r\n- **Automatic FATAL Logging for `uncaughtException`**:\r\n    - Logger automatically registers a handler for `uncaughtException` events\r\n    - When an unhandled exception occurs, logger automatically generates a FATAL level log with tag `[logger] Uncaught exception:`\r\n    - The log includes full stack trace information for quick error location\r\n    - The process will exit after logging (as per Node.js default behavior)\r\n\r\n- **Automatic ERROR Logging for `unhandledRejection`**:\r\n    - Logger automatically registers a handler for `unhandledRejection` events\r\n    - When an unhandled promise rejection occurs, logger automatically generates an ERROR level log with tag `[logger] Unhandled promise rejection:`\r\n    - The log includes stack trace information (extracted from Error objects or auto-generated)\r\n    - The process continues running (does not exit)\r\n\r\n- **Automatic FATAL Logging for `process.exit`**:\r\n    - Logger automatically registers a handler for `process.exit` events\r\n    - When the process exits (via `process.exit()` or natural termination), logger automatically generates a FATAL level log with tag `[logger] Process exit`\r\n    - The log includes the exit code and stack trace information (if available) to help locate where the exit was triggered\r\n    - Uses synchronous file writing to ensure the log is written before the process terminates\r\n\r\n**Note**: Since logger handles these events automatically, you don't need to manually add `process.on('uncaughtException')`, `process.on('unhandledRejection')`, or `process.on('exit')` handlers in your application code. The logger will capture and log these events automatically.\r\n\r\n#### Log File Splitting Mechanism\r\n\r\nLogger implements a concurrent-safe log file splitting mechanism that automatically splits large log files when they exceed the configured size limit. This is especially important in PM2 multi-process environments where multiple processes write to the same log file.\r\n\r\n**How It Works:**\r\n\r\n1. **Timer-Based Periodic Check**: Logger uses a timer-based mechanism (not real-time file size monitoring). A periodic check is scheduled using `setInterval` that runs every `interval` seconds (default: 3600 seconds / 1 hour, configurable via `interval` option). The check also runs immediately when the logger is initialized.\r\n\r\n2. **Size-Based Trigger**: During each periodic check, the logger examines the log file size. **Splitting is only triggered when the file size exceeds the configured limit** (default: 100MB, configurable via `rotate` option). If the file size is below the threshold, no splitting occurs even though the check runs.\r\n\r\n3. **Concurrent-Safe Locking**:\r\n    - Uses a lock file (`.splitting.lock`) with exclusive write mode (`wx`) to ensure only one process performs splitting at a time\r\n    - If the lock file exists, other processes detect it and skip splitting (prevents race conditions)\r\n    - Lock file is automatically released in `finally` block, even if errors occur\r\n    - **Stale Lock Detection**: Automatically detects and removes stale lock files (e.g., when a process crashes). The system checks:\r\n        - Lock file age: If the lock file is older than 5 minutes, it's considered stale\r\n        - Process existence: Checks if the process ID (PID) stored in the lock file is still running\r\n        - Automatic cleanup: Stale locks are automatically removed before attempting to acquire a new lock\r\n\r\n4. **Double-Check Pattern**:\r\n    - After acquiring the lock, re-checks file existence and size\r\n    - If file no longer exists or size is below threshold, releases lock and skips splitting\r\n    - Prevents duplicate splitting when multiple processes detect the need simultaneously\r\n\r\n5. **File Splitting Process**:\r\n    - Reads the entire log file into memory\r\n    - Splits content into multiple files, each not exceeding 90% of the size limit (safety margin)\r\n    - Creates split files with timestamp-based names: `YYYY-MM-DD_HH-MM-SS-N-server.log`\r\n    - Closes file descriptors before deleting the original file\r\n    - Reinitializes file descriptors for continued logging\r\n\r\n6. **Old File Cleanup**:\r\n    - After splitting, checks total number of log files\r\n    - Deletes oldest files if count exceeds `maxFiles` limit (default: 15)\r\n    - Handles concurrent deletion attempts gracefully (detects if file already deleted)\r\n\r\n**Example Scenario (PM2 with 4 processes):**\r\n\r\n```\r\nTime: 18:09:15.890\r\nProcess 1: Detects file size exceeded → Acquires lock → Starts splitting\r\nProcess 2: Detects file size exceeded → Tries to acquire lock → Fails (EEXIST) → Skips\r\nProcess 3: Detects file size exceeded → Tries to acquire lock → Fails (EEXIST) → Skips\r\nProcess 4: Detects file size exceeded → Tries to acquire lock → Fails (EEXIST) → Skips\r\n\r\nTime: 18:09:17.267\r\nProcess 1: Completes splitting → Releases lock → Creates 4 split files\r\nProcess 2-4: Continue normal logging operations\r\n```\r\n\r\n**Configuration:**\r\n\r\n```javascript\r\nconst logger = new Logger({\r\n    rotate: 100, // Split when file exceeds 100MB (checked periodically)\r\n    interval: 3600, // Check every 1 hour (3600 seconds) - timer interval\r\n    maxFiles: 15 // Keep maximum 15 log files\r\n})\r\n```\r\n\r\n**Important Notes:**\r\n\r\n- ⏰ **Timer-Based**: File splitting is **not triggered in real-time** when the file size limit is exceeded. It only happens during the periodic check (default: every 1 hour).\r\n- 📏 **Size Threshold**: The `rotate` option sets the size limit, but splitting only occurs when the periodic check detects the file has exceeded this limit.\r\n- ⚡ **Initial Check**: An immediate check runs when the logger initializes, so splitting can occur right away if the file already exceeds the limit.\r\n- 🔄 **Check Frequency**: For high-volume logging scenarios, consider reducing the `interval` value (minimum: 60 seconds) to check more frequently, but be aware this increases CPU usage.\r\n\r\n**Benefits:**\r\n\r\n- ✅ **Concurrent Safety**: Prevents multiple processes from splitting simultaneously\r\n- ✅ **Data Integrity**: Ensures log files are not corrupted during splitting\r\n- ✅ **Performance**: Only one process performs the expensive splitting operation\r\n- ✅ **Automatic Cleanup**: Old files are automatically removed to save disk space\r\n- ✅ **Error Recovery**: Lock is always released, even if splitting fails\r\n- ✅ **Stale Lock Recovery**: Automatically detects and removes stale locks from crashed processes, ensuring splitting continues to work even after process failures\r\n\r\n#### Runtime Configuration Updates\r\n\r\n```javascript\r\n// Update configuration at runtime (hot reloading)\r\nconst success = logger.updateOptions({\r\n    logLevel: 'debug', // Change log level\r\n    maxLength: 2000, // Increase max message length\r\n    traceLogWrite: true // Enable performance monitoring\r\n})\r\n\r\nif (success) {\r\n    console.log('Configuration updated successfully')\r\n}\r\n\r\n// Get current configuration\r\nconst currentConfig = logger.getOptions()\r\nconsole.log('Current configuration:', currentConfig)\r\n```\r\n\r\n#### Statistics and Monitoring\r\n\r\n```javascript\r\n// Get real-time statistics and monitoring information\r\nconst stats = logger.getStatistics()\r\nconsole.log('Logger Statistics:', stats)\r\n// Output includes: totalLogs (as string), flushedLogs (as string), buffer stats, etc.\r\n// Note: Large numbers are returned as strings to avoid JavaScript number precision limits\r\n\r\n// Reset all statistics counters to zero\r\nlogger.resetStatistics()\r\nconsole.log('Statistics have been reset')\r\n```\r\n\r\n#### Log Reporting\r\n\r\nShip structured JSON logs to OpenObserve over HTTP. Uses Node native `http`/`https` (compatible with Node 12~20), async batch upload, optional timeout retry, concurrency limits, queue cap with drop-oldest, and circuit breaker protection.\r\n\r\n```javascript\r\nconst logger = new Logger({\r\n    logFormat: 'json', // required for reporter\r\n    reporter: {\r\n        enabled: true,\r\n        url: 'http://host:5080/logtracer/api/default/nodejs/_json',\r\n        authorization: 'Basic <access-key>',\r\n        disableLocalFileWrite: false\r\n    }\r\n})\r\n```\r\n\r\n- **`enabled`**: Whether to enable log reporting.\r\n- **`url`**: Log reporting server URL.\r\n- **`authorization`**: Authorization string for HTTP headers.\r\n- **`disableLocalFileWrite`**: Optional remote-only mode — when `reporter.enabled` and this flag are both `true`, local file writes (sync writes, buffers, rotation timers) are skipped; console output and HTTP reporting remain active. Default is `false` (dual write: local file + remote).\r\n- **`normalizePayload`** (v1.4.7+, default `true`): Rewrites HTTP ingest to top-level fields per `extractTopLevelFields` + `rawData` (full second argument). Does not change local file or console JSON.\r\n- **`extractTopLevelFields`** (v1.4.7+, default `['rid']`, max **10**): Business field names to promote to ingest top level.\r\n- **`rawDataAsString`** (v1.4.7+, default `false`): Object-literal string by default; `true` for JSON.stringify.\r\n\r\n### Usage Examples\r\n\r\n#### HTTP Request Logging\r\n\r\n##### Koa Framework Usage\r\n\r\n```javascript\r\nconst logger = new Logger({utcFormat: 8})\r\n\r\n// Basic middleware usage (high-precision timing)\r\napp.use(async (ctx, next) => {\r\n    // Use high-precision timer\r\n    const start = process.hrtime()\r\n    await next()\r\n    // Calculate and format precise response time (milliseconds, max 3 decimal places)\r\n    // Format examples: 1.030 -> 1.03, 1.031 -> 1.031, 1.000 -> 1\r\n    const [seconds, nanoseconds] = process.hrtime(start)\r\n    let responseTime = seconds * 1000 + nanoseconds / 1000000\r\n    // Format: max 3 decimal places, remove trailing zeros\r\n    responseTime = parseFloat(responseTime.toFixed(3))\r\n\r\n    // Automatically parse all information from ctx\r\n    logger.logHttpRequest(ctx, {\r\n        frameworkStyle: 'koa', // Required: must specify framework type\r\n        responseTime: responseTime\r\n    })\r\n})\r\n\r\n// With extra parameters (high-precision timing)\r\napp.use(async (ctx, next) => {\r\n    // Use high-precision timer\r\n    const start = process.hrtime()\r\n    await next()\r\n    // Calculate and format precise response time (milliseconds, max 3 decimal places)\r\n    // Format examples: 1.030 -> 1.03, 1.031 -> 1.031, 1.000 -> 1\r\n    const [seconds, nanoseconds] = process.hrtime(start)\r\n    let responseTime = seconds * 1000 + nanoseconds / 1000000\r\n    // Format: max 3 decimal places, remove trailing zeros\r\n    responseTime = parseFloat(responseTime.toFixed(3))\r\n\r\n    // Log user information and request details\r\n    logger.logHttpRequest(ctx, {\r\n        frameworkStyle: 'koa', // Required: must specify framework type\r\n        responseTime: responseTime,\r\n        extraParams: [`userId:${ctx.state.user?.id || 'anonymous'}`, `userAgent:${ctx.get('User-Agent')}`]\r\n    })\r\n})\r\n```\r\n\r\n##### Express Framework Usage\r\n\r\n```javascript\r\nconst logger = new Logger({utcFormat: 8})\r\n\r\n// Basic middleware usage (high-precision timing)\r\napp.use((req, res, next) => {\r\n    // Use high-precision timer\r\n    const start = process.hrtime()\r\n\r\n    res.on('finish', () => {\r\n        // Calculate precise response time (milliseconds, max 3 decimal places)\r\n        // Format examples: 1.030 -> 1.03, 1.031 -> 1.031, 1.000 -> 1\r\n        const [seconds, nanoseconds] = process.hrtime(start)\r\n        const responseTime = seconds * 1000 + nanoseconds / 1000000\r\n        // Format: max 3 decimal places, remove trailing zeros\r\n        responseTime = parseFloat(responseTime.toFixed(3))\r\n\r\n        // Use logHttpRequest to log HTTP request information\r\n        logger.logHttpRequest(\r\n            {req, res},\r\n            {\r\n                frameworkStyle: 'express',\r\n                responseTime: responseTime\r\n            }\r\n        )\r\n    })\r\n\r\n    next()\r\n})\r\n\r\n// With extra parameters (high-precision timing)\r\napp.use((req, res, next) => {\r\n    // Use high-precision timer\r\n    const start = process.hrtime()\r\n\r\n    res.on('finish', () => {\r\n        // Calculate precise response time (milliseconds, max 3 decimal places)\r\n        // Format examples: 1.030 -> 1.03, 1.031 -> 1.031, 1.000 -> 1\r\n        const [seconds, nanoseconds] = process.hrtime(start)\r\n        const responseTime = seconds * 1000 + nanoseconds / 1000000\r\n        // Format: max 3 decimal places, remove trailing zeros\r\n        responseTime = parseFloat(responseTime.toFixed(3))\r\n\r\n        logger.logHttpRequest(\r\n            {req, res},\r\n            {\r\n                frameworkStyle: 'express', // Required: must specify framework type\r\n                responseTime: responseTime,\r\n                extraParams: [`userId:${req.user?.id || 'anonymous'}`, `sessionId:${req.session?.id || 'none'}`]\r\n            }\r\n        )\r\n    })\r\n\r\n    next()\r\n})\r\n```\r\n\r\n##### Output Examples\r\n\r\n```\r\n2025-09-20 17:53:01.113 UTC+8 GET /api/users 200 125.68 ms 192.168.1.100 - 12345 [userId:123]\r\n2025-09-20 17:53:01.114 UTC+8 POST /api/login 401 234.57 ms 10.0.0.50 - 12345 [userId:anonymous]\r\n2025-09-20 17:53:01.115 UTC+8 PUT /api/user 200 100 ms 192.168.1.100 - 12345\r\n```\r\n\r\n**Format explanation:**\r\n\r\n- Timestamp: UTC+8 timezone, high-precision milliseconds\r\n- Request method: HTTP method\r\n- Path: Request path\r\n- Status code: HTTP response status code\r\n- Response time: **milliseconds** (fixed unit), 3 decimal places precision\r\n- IP address: Real client IP\r\n- Process ID: Node.js process ID\r\n- Extra parameters: [Optional] User ID and other information\r\n\r\n#### Error Handling\r\n\r\n```javascript\r\n// Note: Logger automatically handles uncaughtException and unhandledRejection events\r\n// You don't need to manually register these handlers - logger does it automatically\r\n\r\n// Business error objects (e.g., {code, msg, data}) are fully supported\r\nlogger.error('Controller.RealTime.pay', {\r\n    error: {code: 2005, msg: 'Order not found', data: 'order_id: 12345'},\r\n    data: {type: '2', order_id: '123'},\r\n    msg: 'catch error'\r\n})\r\n// All properties (code, msg, data) are correctly serialized and logged\r\n```\r\n\r\n#### Database Logging\r\n\r\n```javascript\r\nconst dbLogger = logger.createChildLogger('Database')\r\nasync function queryDatabase(sql, params) {\r\n    try {\r\n        const start = Date.now()\r\n        const result = await db.query(sql, params)\r\n        const duration = Date.now() - start\r\n        dbLogger.info({sql, params, duration: `${duration}ms`, rows: result.length})\r\n        return result\r\n    } catch (error) {\r\n        dbLogger.error('Database query error', {sql, params, error: error.message})\r\n        throw error\r\n    }\r\n}\r\n```\r\n\r\n#### Custom Ctx Processing\r\n\r\nYou can customize how `traceRequestPerformance` processes the `ctx` parameter by providing a custom processor function. This is particularly useful for **internal RPC communication** between services where you need specific context filtering:\r\n\r\n```javascript\r\nconst Logger = require('bandeng-logger')\r\n\r\n// 1. Using predefined processors\r\nconst logger1 = new Logger({\r\n    logLevel: 'info',\r\n    ctxProcessor: Logger.getCtxProcessor('whitelist', ['userId', 'ip', 'method'])\r\n})\r\n\r\n// 2. Using whitelist with nested field paths (supports dot notation)\r\nconst loggerNested = new Logger({\r\n    logLevel: 'info',\r\n    ctxProcessor: Logger.getCtxProcessor('whitelist', ['user.id', 'user.name', 'request.method'])\r\n})\r\n\r\n// 3. Using blacklist to exclude sensitive fields (supports nested paths)\r\nconst logger2 = new Logger({\r\n    logLevel: 'info',\r\n    ctxProcessor: Logger.getCtxProcessor('blacklist', ['user.password', 'auth.token', 'sensitive.data'])\r\n})\r\n\r\n// 4. Field renaming\r\nconst logger3 = new Logger({\r\n    logLevel: 'info',\r\n    ctxProcessor: Logger.getCtxProcessor('rename', {\r\n        userId: 'user_id',\r\n        sessionId: 'session_id'\r\n    })\r\n})\r\n\r\n// 4. Complex transformation\r\nconst logger4 = new Logger({\r\n    logLevel: 'info',\r\n    ctxProcessor: Logger.getCtxProcessor('transform', {\r\n        include: ['userId', 'ip', 'headers'],\r\n        exclude: ['authorization'],\r\n        rename: {\r\n            userId: 'user_id',\r\n            headers: 'request_headers'\r\n        }\r\n    })\r\n})\r\n\r\n// 5. Custom processor function\r\nconst logger5 = new Logger({\r\n    logLevel: 'info',\r\n    ctxProcessor: ctx => {\r\n        // Custom processing logic\r\n        const result = {}\r\n        for (const [key, value] of Object.entries(ctx)) {\r\n            if (typeof value === 'string' && key !== 'sensitive') {\r\n                result[key] = value.toUpperCase()\r\n            }\r\n        }\r\n        return result\r\n    }\r\n})\r\n\r\n// Usage example\r\nconst requestCtx = {\r\n    userId: 'user123',\r\n    ip: '192.168.1.100',\r\n    method: 'POST',\r\n    password: 'secret',\r\n    headers: {'content-type': 'application/json'}\r\n}\r\n\r\nlogger1.traceRequestPerformance(requestCtx, 150, 100) // Only logs userId, ip, method\r\nlogger2.traceRequestPerformance(requestCtx, 150, 100) // Excludes password field\r\n```\r\n\r\n**Nested Field Path Support:**\r\n\r\nBoth `whitelist` and `blacklist` processors support nested field paths using dot notation:\r\n\r\n- `'user.id'` - matches `ctx.user.id`\r\n- `'request.headers.authorization'` - matches `ctx.request.headers.authorization`\r\n- `'data.items[0].name'` - matches `ctx.data.items[0].name`\r\n\r\n**Examples with nested paths:**\r\n\r\n```javascript\r\n// Include nested user fields only\r\nconst userLogger = new Logger({\r\n    ctxProcessor: Logger.getCtxProcessor('whitelist', ['user.id', 'user.name', 'user.email'])\r\n})\r\n\r\n// Exclude nested sensitive data\r\nconst secureLogger = new Logger({\r\n    ctxProcessor: Logger.getCtxProcessor('blacklist', ['user.password', 'auth.token', 'payment.cardNumber'])\r\n})\r\n```\r\n\r\n#### Force Print Conditions (v1.3.0)\r\n\r\nForce print conditions allow you to override log level restrictions for specific logs that match certain criteria. This is extremely useful for **business debugging** and **troubleshooting** in production environments.\r\n\r\n**Key Features:**\r\n\r\n- ✅ Override log levels - logs matching conditions print regardless of configured log level\r\n- ✅ Function-based conditions - support complex logic and multiple criteria\r\n- ✅ Context information - access to level, message, and timestamp\r\n- ✅ Error-safe - individual condition failures don't break logging\r\n- ✅ Performance optimized - conditions only evaluated when log level filtering fails\r\n\r\n**Basic Usage:**\r\n\r\n```javascript\r\nconst logger = new Logger({\r\n    logLevel: 'error', // Only error+ levels normally print\r\n    forcePrintConditions: [\r\n        // Force print all logs containing 'payment'\r\n        logData => logData.message.includes('payment'),\r\n        // Force print all debug level login-related logs\r\n        logData => logData.level === 'debug' && logData.message.includes('login')\r\n    ]\r\n})\r\n\r\n// Usage examples:\r\nlogger.error('Payment processing failed') // ✅ Prints (level >= error)\r\nlogger.debug('User payment successful') // ✅ Prints (matches 'payment' condition)\r\nlogger.debug('User login successful') // ✅ Prints (debug + login condition)\r\nlogger.debug('Normal debug message') // ❌ Doesn't print (no matching condition)\r\nlogger.info('Normal info message') // ❌ Doesn't print (level < error)\r\n```\r\n\r\n**Advanced Examples:**\r\n\r\n**1. Time-window Based Conditions:**\r\n\r\n```javascript\r\nconst logger = new Logger({\r\n    logLevel: 'warn',\r\n    forcePrintConditions: [\r\n        // Force print all errors in the last 5 minutes\r\n        logData => logData.level === 'error' && logData.timestamp > Date.now() - 5 * 60 * 1000,\r\n        // Force print payment logs for next 1 hour\r\n        logData => logData.message.includes('payment') && logData.timestamp < Date.now() + 60 * 60 * 1000\r\n    ]\r\n})\r\n```\r\n\r\n**2. Complex Business Logic:**\r\n\r\n```javascript\r\nconst logger = new Logger({\r\n    logLevel: 'error',\r\n    forcePrintConditions: [\r\n        // Force print user authentication failures\r\n        logData => logData.level === 'warn' && logData.message.match(/login.*fail|auth.*error/i),\r\n        // Force print high-value transaction logs\r\n        logData => {\r\n            const msg = logData.message.toLowerCase()\r\n            return msg.includes('transaction') && (msg.includes('amount:') || msg.includes('$'))\r\n        },\r\n        // Force print database errors with specific patterns\r\n        logData => logData.level === 'error' && logData.message.includes('database') && logData.message.includes('timeout')\r\n    ]\r\n})\r\n```\r\n\r\n**3. Environment-Based Conditions:**\r\n\r\n```javascript\r\nconst isProduction = process.env.NODE_ENV === 'production'\r\nconst debugUserIds = process.env.DEBUG_USER_IDS?.split(',') || []\r\n\r\nconst logger = new Logger({\r\n    logLevel: isProduction ? 'warn' : 'debug',\r\n    forcePrintConditions: [\r\n        // In production, force print logs for specific debug users\r\n        ...(isProduction\r\n            ? [\r\n                  logData => {\r\n                      const userIdMatch = logData.message.match(/user[_\\s]+(\\d+)/i)\r\n                      return userIdMatch && debugUserIds.includes(userIdMatch[1])\r\n                  }\r\n              ]\r\n            : [])\r\n    ]\r\n})\r\n```\r\n\r\n**4. Performance Monitoring:**\r\n\r\n```javascript\r\nconst logger = new Logger({\r\n    logLevel: 'info',\r\n    forcePrintConditions: [\r\n        // Force print slow operations regardless of level\r\n        logData => {\r\n            const durationMatch = logData.message.match(/(\\d+)ms/i)\r\n            return durationMatch && parseInt(durationMatch[1]) > 1000 // > 1 second\r\n        },\r\n        // Force print memory warnings\r\n        logData => logData.message.includes('memory') && logData.level === 'warn'\r\n    ]\r\n})\r\n```\r\n\r\n**5. Service-Specific Debugging:**\r\n\r\n```javascript\r\nconst logger = new Logger({\r\n    logLevel: 'error',\r\n    forcePrintConditions: [\r\n        // Debug payment service issues\r\n        logData => logData.message.includes('payment-service') && ['error', 'warn'].includes(logData.level),\r\n        // Debug user session problems\r\n        logData => logData.message.match(/session.*(?:expired|invalid|timeout)/i),\r\n        // Debug API rate limiting\r\n        logData => logData.message.includes('rate limit') || logData.message.includes('429')\r\n    ]\r\n})\r\n```\r\n\r\n**Condition Function Parameters:**\r\n\r\nEach condition function receives a `logData` object with:\r\n\r\n- `level`: Log level ('fatal', 'error', 'warn', 'info', 'debug', 'trace')\r\n- `message`: Log message string\r\n- `timestamp`: Log timestamp (Date.now() format)\r\n\r\n**Best Practices:**\r\n\r\n1. **Keep conditions simple** - Complex conditions can impact performance\r\n2. **Use specific patterns** - Avoid overly broad conditions that print too much\r\n3. **Time-limit conditions** - Use timestamps to automatically disable debug logging\r\n4. **Monitor impact** - Force printing can generate significant log volume\r\n5. **Clean up** - Remove or disable conditions when debugging is complete\r\n\r\n**Performance Notes:**\r\n\r\n- Conditions are only evaluated when log level filtering would normally suppress the log\r\n- Failed condition functions are safely caught and don't break logging\r\n- Use efficient string operations (includes, startsWith) over complex regex when possible\r\n\r\n### Comparison with Other Logging Libraries\r\n\r\n| Feature                       | Bandeng Logger | Winston     | Pino        | Log4js      | Bunyan      |\r\n| ----------------------------- | -------------- | ----------- | ----------- | ----------- | ----------- |\r\n| Log Rotation                  | ✅ Built-in    | ❌ Plugin   | ❌ Plugin   | ✅ Built-in | ❌ Plugin   |\r\n| Performance Monitor           | ✅ Built-in    | ❌ Plugin   | ❌ Plugin   | ❌ Plugin   | ❌ Plugin   |\r\n| PM2 Support                   | ✅ Built-in    | ❌ Plugin   | ❌ Plugin   | ❌ Plugin   | ❌ Plugin   |\r\n| Zero-blocking I/O             | ✅ Built-in    | ❌ Plugin   | ❌ Plugin   | ❌ Plugin   | ❌ Plugin   |\r\n| High-precision Timestamps     | ✅ Built-in    | ❌ Plugin   | ❌ Plugin   | ❌ Plugin   | ❌ Plugin   |\r\n| Concurrent Safety             | ✅ Built-in    | ❌ Plugin   | ❌ Plugin   | ❌ Plugin   | ❌ Plugin   |\r\n| Real-time Statistics          | ✅ Built-in    | ❌ Plugin   | ❌ Plugin   | ❌ Plugin   | ❌ Plugin   |\r\n| Smart Buffering               | ✅ Built-in    | ❌ Plugin   | ❌ Plugin   | ❌ Plugin   | ❌ Plugin   |\r\n| Child logger                  | ✅ Built-in    | ✅ Built-in | ✅ Built-in | ✅ Built-in | ✅ Built-in |\r\n| Color Console Output          | ✅ Built-in    | ❌ Plugin   | ❌ Plugin   | ✅ Built-in | ❌ Plugin   |\r\n| Log Compression               | ✅ Built-in    | ❌ Plugin   | ❌ Plugin   | ✅ Built-in | ❌ Plugin   |\r\n| UTC Timezone Support          | ✅ Built-in    | ❌ Plugin   | ❌ Plugin   | ❌ Plugin   | ❌ Plugin   |\r\n| Error Log Separation          | ✅ Built-in    | ❌ Plugin   | ❌ Plugin   | ❌ Plugin   | ❌ Plugin   |\r\n| Log Write Performance Monitor | ✅ Built-in    | ❌ Plugin   | ❌ Plugin   | ❌ Plugin   | ❌ Plugin   |\r\n\r\n### API Reference\r\n\r\n#### Logger Methods\r\n\r\n- `fatal(message, ...args)` - Log fatal error\r\n- `error(message, ...args)` - Log error\r\n- `warn(message, ...args)` - Log warning\r\n- `info(message, ...args)` - Log info\r\n- `debug(message, ...args)` - Log debug\r\n- `trace(message, ...args)` - Log trace\r\n\r\n#### Child Logger Methods\r\n\r\n- `createChildLogger(moduleName)` - Create a child logger with module name prefix\r\n\r\n#### Performance Monitoring Methods\r\n\r\n- `traceRequestPerformance(ctx, latency, timeThreshold, [forcePrint], [tag])` - Advanced request performance tracking for **internal RPC communication** between services, with context sanitization and custom tags\r\n- `Logger.getCtxProcessor(type, options)` - Get predefined ctx processing functions for traceRequestPerformance\r\n\r\n##### Ctx Processor Types\r\n\r\n- `whitelist` - Only include specified fields\r\n- `blacklist` - Exclude specified fields\r\n- `rename` - Rename fields using mapping object\r\n- `transform` - Complex transformation with include/","readmeFilename":"README.md"}