feat(logging): add Seq transport, global meta fields, and improve error handling
- Add Seq centralized logging transport with async ESM import - Add appVersion and computerName to logger defaultMeta (all app logs) - Add appVersion to audit log entries for version-level traceability - Improve unhandledRejection to capture full stack traces for Error instances - Add Seq config schema and template configuration Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -63,6 +63,16 @@ logging:
|
|||||||
auditRetention: 30
|
auditRetention: 30
|
||||||
appRetention: 14
|
appRetention: 14
|
||||||
|
|
||||||
|
# Seq 日志聚合服务配置(可选,用于集中管理日志)
|
||||||
|
seq:
|
||||||
|
enabled: false # 设置为 true 启用 Seq 日志发送
|
||||||
|
serverUrl: 'http://localhost:5341' # Seq 服务器地址
|
||||||
|
apiKey: '' # 可选:API key 用于认证
|
||||||
|
batchPostingLimit: 50 # 每批次最大日志条目数
|
||||||
|
period: 2000 # 发送间隔 (毫秒)
|
||||||
|
queueLimit: 10000 # 本地队列最大容量
|
||||||
|
maxRetries: 3 # 失败重试次数
|
||||||
|
|
||||||
# RustFS 对象存储配置(用于持久化报告)
|
# RustFS 对象存储配置(用于持久化报告)
|
||||||
rustfs:
|
rustfs:
|
||||||
enabled: false # 设置为 true 启用 RustFS 上传
|
enabled: false # 设置为 true 启用 RustFS 上传
|
||||||
|
|||||||
124
package-lock.json
generated
124
package-lock.json
generated
@@ -10,6 +10,7 @@
|
|||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.929.0",
|
"@aws-sdk/client-s3": "^3.929.0",
|
||||||
|
"@datalust/winston-seq": "^3.0.1",
|
||||||
"@electron-toolkit/preload": "^3.0.2",
|
"@electron-toolkit/preload": "^3.0.2",
|
||||||
"@electron-toolkit/utils": "^4.0.0",
|
"@electron-toolkit/utils": "^4.0.0",
|
||||||
"@headlessui/react": "^2.2.9",
|
"@headlessui/react": "^2.2.9",
|
||||||
@@ -970,6 +971,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.10.1.tgz",
|
||||||
"integrity": "sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w==",
|
"integrity": "sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@azure/abort-controller": "^2.1.2",
|
"@azure/abort-controller": "^2.1.2",
|
||||||
"@azure/core-auth": "^1.10.0",
|
"@azure/core-auth": "^1.10.0",
|
||||||
@@ -1031,6 +1033,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.22.2.tgz",
|
"resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.22.2.tgz",
|
||||||
"integrity": "sha512-MzHym+wOi8CLUlKCQu12de0nwcq9k9Kuv43j4Wa++CsCpJwps2eeBQwD2Bu8snkxTtDKDx4GwjuR9E8yC8LNrg==",
|
"integrity": "sha512-MzHym+wOi8CLUlKCQu12de0nwcq9k9Kuv43j4Wa++CsCpJwps2eeBQwD2Bu8snkxTtDKDx4GwjuR9E8yC8LNrg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@azure/abort-controller": "^2.1.2",
|
"@azure/abort-controller": "^2.1.2",
|
||||||
"@azure/core-auth": "^1.10.0",
|
"@azure/core-auth": "^1.10.0",
|
||||||
@@ -1222,6 +1225,7 @@
|
|||||||
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.29.0",
|
"@babel/code-frame": "^7.29.0",
|
||||||
"@babel/generator": "^7.29.0",
|
"@babel/generator": "^7.29.0",
|
||||||
@@ -1528,6 +1532,19 @@
|
|||||||
"kuler": "^2.0.0"
|
"kuler": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@datalust/winston-seq": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@datalust/winston-seq/-/winston-seq-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-jWJd5PKcj/nM5f1T65KJgKaxPJRADWe+GEWtj1yEji1H0ub4RWhBEDLYzIFdwUy365lxtc5njsakenp4Evmv+g==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"seq-logging": "^3.0.0",
|
||||||
|
"winston-transport": "^4.9.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"winston": "^3.17.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@develar/schema-utils": {
|
"node_modules/@develar/schema-utils": {
|
||||||
"version": "2.6.5",
|
"version": "2.6.5",
|
||||||
"resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz",
|
"resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz",
|
||||||
@@ -1999,7 +2016,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cross-dirname": "^0.1.0",
|
"cross-dirname": "^0.1.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
@@ -2021,7 +2037,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"graceful-fs": "^4.2.0",
|
"graceful-fs": "^4.2.0",
|
||||||
"jsonfile": "^6.0.1",
|
"jsonfile": "^6.0.1",
|
||||||
@@ -2038,7 +2053,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"universalify": "^2.0.0"
|
"universalify": "^2.0.0"
|
||||||
},
|
},
|
||||||
@@ -2053,7 +2067,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10.0.0"
|
"node": ">= 10.0.0"
|
||||||
}
|
}
|
||||||
@@ -5002,6 +5015,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.13.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.13.tgz",
|
||||||
"integrity": "sha512-akNQMv0wW5uyRpD2v2IEyRSZiR+BeGuoB6L310EgGObO44HSMNT8z1xzio28V8qOrgYaopIDNA18YgdXd+qTiw==",
|
"integrity": "sha512-akNQMv0wW5uyRpD2v2IEyRSZiR+BeGuoB6L310EgGObO44HSMNT8z1xzio28V8qOrgYaopIDNA18YgdXd+qTiw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~6.21.0"
|
"undici-types": "~6.21.0"
|
||||||
}
|
}
|
||||||
@@ -5023,6 +5037,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
|
||||||
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
|
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.2.2"
|
"csstype": "^3.2.2"
|
||||||
}
|
}
|
||||||
@@ -5143,6 +5158,7 @@
|
|||||||
"integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==",
|
"integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.56.1",
|
"@typescript-eslint/scope-manager": "8.56.1",
|
||||||
"@typescript-eslint/types": "8.56.1",
|
"@typescript-eslint/types": "8.56.1",
|
||||||
@@ -5582,6 +5598,7 @@
|
|||||||
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
|
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
@@ -5614,6 +5631,7 @@
|
|||||||
"integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
|
"integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-deep-equal": "^3.1.1",
|
"fast-deep-equal": "^3.1.1",
|
||||||
"fast-json-stable-stringify": "^2.0.0",
|
"fast-json-stable-stringify": "^2.0.0",
|
||||||
@@ -6376,6 +6394,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.9.0",
|
"baseline-browser-mapping": "^2.9.0",
|
||||||
"caniuse-lite": "^1.0.30001759",
|
"caniuse-lite": "^1.0.30001759",
|
||||||
@@ -7171,8 +7190,7 @@
|
|||||||
"integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==",
|
"integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/cross-spawn": {
|
"node_modules/cross-spawn": {
|
||||||
"version": "7.0.6",
|
"version": "7.0.6",
|
||||||
@@ -7705,6 +7723,7 @@
|
|||||||
"integrity": "sha512-glMJgnTreo8CFINujtAhCgN96QAqApDMZ8Vl1r8f0QT8QprvC1UCltV4CcWj20YoIyLZx6IUskaJZ0NV8fokcg==",
|
"integrity": "sha512-glMJgnTreo8CFINujtAhCgN96QAqApDMZ8Vl1r8f0QT8QprvC1UCltV4CcWj20YoIyLZx6IUskaJZ0NV8fokcg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"app-builder-lib": "26.8.1",
|
"app-builder-lib": "26.8.1",
|
||||||
"builder-util": "26.8.1",
|
"builder-util": "26.8.1",
|
||||||
@@ -7919,6 +7938,7 @@
|
|||||||
"integrity": "sha512-Rz5QvP1pTqoU1DPRrG3EeX2oWBtS3uRmd6Z/wzZsb2e/iIUsrT+XcBaAhFr4FW48gDc8uP2wYVyY5Aamha/5Zg==",
|
"integrity": "sha512-Rz5QvP1pTqoU1DPRrG3EeX2oWBtS3uRmd6Z/wzZsb2e/iIUsrT+XcBaAhFr4FW48gDc8uP2wYVyY5Aamha/5Zg==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron/get": "^2.0.0",
|
"@electron/get": "^2.0.0",
|
||||||
"@types/node": "^22.7.7",
|
"@types/node": "^22.7.7",
|
||||||
@@ -8107,7 +8127,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron/asar": "^3.2.1",
|
"@electron/asar": "^3.2.1",
|
||||||
"debug": "^4.1.1",
|
"debug": "^4.1.1",
|
||||||
@@ -8128,7 +8147,6 @@
|
|||||||
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
|
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"graceful-fs": "^4.1.2",
|
"graceful-fs": "^4.1.2",
|
||||||
"jsonfile": "^4.0.0",
|
"jsonfile": "^4.0.0",
|
||||||
@@ -8150,17 +8168,6 @@
|
|||||||
"integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==",
|
"integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/encoding": {
|
|
||||||
"version": "0.1.13",
|
|
||||||
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
|
|
||||||
"integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"iconv-lite": "^0.6.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/end-of-stream": {
|
"node_modules/end-of-stream": {
|
||||||
"version": "1.4.5",
|
"version": "1.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
|
||||||
@@ -8467,6 +8474,7 @@
|
|||||||
"integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==",
|
"integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.8.0",
|
"@eslint-community/eslint-utils": "^4.8.0",
|
||||||
"@eslint-community/regexpp": "^4.12.1",
|
"@eslint-community/regexpp": "^4.12.1",
|
||||||
@@ -8527,6 +8535,7 @@
|
|||||||
"integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
|
"integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"eslint-config-prettier": "bin/cli.js"
|
"eslint-config-prettier": "bin/cli.js"
|
||||||
},
|
},
|
||||||
@@ -9955,7 +9964,7 @@
|
|||||||
"version": "0.6.3",
|
"version": "0.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||||
@@ -12978,6 +12987,26 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/node-fetch": {
|
||||||
|
"version": "2.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||||
|
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"whatwg-url": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "4.x || >=6.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"encoding": "^0.1.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"encoding": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/node-gyp": {
|
"node_modules/node-gyp": {
|
||||||
"version": "11.5.0",
|
"version": "11.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-11.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-11.5.0.tgz",
|
||||||
@@ -13514,6 +13543,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -13594,6 +13624,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.11",
|
"nanoid": "^3.3.11",
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.1.1",
|
||||||
@@ -13617,7 +13648,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"commander": "^9.4.0"
|
"commander": "^9.4.0"
|
||||||
},
|
},
|
||||||
@@ -13635,7 +13665,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^12.20.0 || >=14"
|
"node": "^12.20.0 || >=14"
|
||||||
}
|
}
|
||||||
@@ -13656,6 +13685,7 @@
|
|||||||
"integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
|
"integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"prettier": "bin/prettier.cjs"
|
"prettier": "bin/prettier.cjs"
|
||||||
},
|
},
|
||||||
@@ -13797,6 +13827,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
|
||||||
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
|
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -13818,6 +13849,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
|
||||||
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
|
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"scheduler": "^0.27.0"
|
"scheduler": "^0.27.0"
|
||||||
},
|
},
|
||||||
@@ -13852,7 +13884,8 @@
|
|||||||
"version": "16.13.1",
|
"version": "16.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/react-markdown": {
|
"node_modules/react-markdown": {
|
||||||
"version": "10.1.0",
|
"version": "10.1.0",
|
||||||
@@ -13886,6 +13919,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
|
||||||
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
|
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/use-sync-external-store": "^0.0.6",
|
"@types/use-sync-external-store": "^0.0.6",
|
||||||
"use-sync-external-store": "^1.4.0"
|
"use-sync-external-store": "^1.4.0"
|
||||||
@@ -14011,7 +14045,8 @@
|
|||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
|
||||||
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
|
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/redux-thunk": {
|
"node_modules/redux-thunk": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
@@ -14540,6 +14575,19 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"node_modules/seq-logging": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/seq-logging/-/seq-logging-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-ys5QV0745vxBCWuZBPSkgoobuLoUMxTSz1g7ZclHqX1tXXKFLyRIIn8V89EPgDnfRiWfoSo4KSxy/E0MtOYYyw==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"abort-controller": "^3.0.0",
|
||||||
|
"node-fetch": "^2.7.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/serialize-error": {
|
"node_modules/serialize-error": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz",
|
||||||
@@ -15405,7 +15453,6 @@
|
|||||||
"integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==",
|
"integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
"rimraf": "~2.6.2"
|
"rimraf": "~2.6.2"
|
||||||
@@ -15571,6 +15618,12 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tr46": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||||
|
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/traverse": {
|
"node_modules/traverse": {
|
||||||
"version": "0.3.9",
|
"version": "0.3.9",
|
||||||
"resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz",
|
"resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz",
|
||||||
@@ -16441,6 +16494,7 @@
|
|||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@@ -16878,6 +16932,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
|
||||||
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
|
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.27.0",
|
"esbuild": "^0.27.0",
|
||||||
"fdir": "^6.5.0",
|
"fdir": "^6.5.0",
|
||||||
@@ -17424,6 +17479,7 @@
|
|||||||
"integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==",
|
"integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/expect": "4.0.18",
|
"@vitest/expect": "4.0.18",
|
||||||
"@vitest/mocker": "4.0.18",
|
"@vitest/mocker": "4.0.18",
|
||||||
@@ -17506,6 +17562,22 @@
|
|||||||
"defaults": "^1.0.3"
|
"defaults": "^1.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/webidl-conversions": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||||
|
"license": "BSD-2-Clause"
|
||||||
|
},
|
||||||
|
"node_modules/whatwg-url": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tr46": "~0.0.3",
|
||||||
|
"webidl-conversions": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz",
|
||||||
@@ -17632,6 +17704,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/winston/-/winston-3.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/winston/-/winston-3.19.0.tgz",
|
||||||
"integrity": "sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==",
|
"integrity": "sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@colors/colors": "^1.6.0",
|
"@colors/colors": "^1.6.0",
|
||||||
"@dabh/diagnostics": "^2.0.8",
|
"@dabh/diagnostics": "^2.0.8",
|
||||||
@@ -17869,6 +17942,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
|
||||||
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
|
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.929.0",
|
"@aws-sdk/client-s3": "^3.929.0",
|
||||||
|
"@datalust/winston-seq": "^3.0.1",
|
||||||
"@electron-toolkit/preload": "^3.0.2",
|
"@electron-toolkit/preload": "^3.0.2",
|
||||||
"@electron-toolkit/utils": "^4.0.0",
|
"@electron-toolkit/utils": "^4.0.0",
|
||||||
"@headlessui/react": "^2.2.9",
|
"@headlessui/react": "^2.2.9",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { app } from 'electron'
|
import { app } from 'electron'
|
||||||
import logger from '../services/logger/index'
|
import logger from '../services/logger/index'
|
||||||
import { logAudit, closeAuditLogger } from '../services/logger/audit-logger'
|
import { logAudit, closeAuditLogger } from '../services/logger/audit-logger'
|
||||||
|
import { serializeError } from '../services/logger/error-utils'
|
||||||
|
|
||||||
export function setupProcessGuards(): void {
|
export function setupProcessGuards(): void {
|
||||||
process.on('uncaughtException', (err) => {
|
process.on('uncaughtException', (err) => {
|
||||||
@@ -16,13 +17,17 @@ export function setupProcessGuards(): void {
|
|||||||
})
|
})
|
||||||
|
|
||||||
process.on('unhandledRejection', (reason) => {
|
process.on('unhandledRejection', (reason) => {
|
||||||
logger.error('Unhandled Rejection', { reason: String(reason) })
|
const errorMeta =
|
||||||
|
reason instanceof Error
|
||||||
|
? { error: serializeError(reason) }
|
||||||
|
: { reason: String(reason) }
|
||||||
|
logger.error('Unhandled Rejection', errorMeta)
|
||||||
logAudit('SYSTEM_ERROR', 'system', {
|
logAudit('SYSTEM_ERROR', 'system', {
|
||||||
username: 'system',
|
username: 'system',
|
||||||
computerName: process.env.COMPUTERNAME || 'unknown',
|
computerName: process.env.COMPUTERNAME || 'unknown',
|
||||||
resource: 'main-process',
|
resource: 'main-process',
|
||||||
status: 'failure',
|
status: 'failure',
|
||||||
metadata: { reason: String(reason) }
|
metadata: errorMeta
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -102,6 +102,15 @@ const DEFAULT_CONFIG: FullConfig = {
|
|||||||
auditRetention: 30,
|
auditRetention: 30,
|
||||||
appRetention: 14
|
appRetention: 14
|
||||||
},
|
},
|
||||||
|
seq: {
|
||||||
|
enabled: false,
|
||||||
|
serverUrl: '',
|
||||||
|
apiKey: '',
|
||||||
|
batchPostingLimit: 50,
|
||||||
|
period: 2000,
|
||||||
|
queueLimit: 10000,
|
||||||
|
maxRetries: 3
|
||||||
|
},
|
||||||
rustfs: {
|
rustfs: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
endpoint: '',
|
endpoint: '',
|
||||||
@@ -207,7 +216,7 @@ export class ConfigManager {
|
|||||||
this.config = validated
|
this.config = validated
|
||||||
|
|
||||||
// Apply logging configuration
|
// Apply logging configuration
|
||||||
applyLoggingConfig(validated.logging)
|
applyLoggingConfig(validated.logging, validated.seq)
|
||||||
applyAuditConfig(validated.logging.auditRetention)
|
applyAuditConfig(validated.logging.auditRetention)
|
||||||
|
|
||||||
log.info('Configuration loaded and validated successfully', {
|
log.info('Configuration loaded and validated successfully', {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
import winston from 'winston'
|
import winston from 'winston'
|
||||||
import DailyRotateFile from 'winston-daily-rotate-file'
|
import DailyRotateFile from 'winston-daily-rotate-file'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
import { app } from 'electron'
|
||||||
import { getLogDir } from './shared'
|
import { getLogDir } from './shared'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -23,6 +24,8 @@ export interface AuditEntry {
|
|||||||
username: string
|
username: string
|
||||||
/** Computer name from which the action was performed */
|
/** Computer name from which the action was performed */
|
||||||
computerName: string
|
computerName: string
|
||||||
|
/** Application version when the action was performed */
|
||||||
|
appVersion: string
|
||||||
/** The resource that was affected (e.g., table name, file path) */
|
/** The resource that was affected (e.g., table name, file path) */
|
||||||
resource: string
|
resource: string
|
||||||
/** Status of the action: 'success' | 'failure' | 'partial' */
|
/** Status of the action: 'success' | 'failure' | 'partial' */
|
||||||
@@ -104,6 +107,7 @@ export function logAudit(
|
|||||||
userId,
|
userId,
|
||||||
username: details.username,
|
username: details.username,
|
||||||
computerName: details.computerName,
|
computerName: details.computerName,
|
||||||
|
appVersion: app.getVersion(),
|
||||||
resource: details.resource,
|
resource: details.resource,
|
||||||
status: details.status,
|
status: details.status,
|
||||||
metadata: details.metadata || {}
|
metadata: details.metadata || {}
|
||||||
|
|||||||
@@ -11,11 +11,14 @@
|
|||||||
import winston from 'winston'
|
import winston from 'winston'
|
||||||
import DailyRotateFile from 'winston-daily-rotate-file'
|
import DailyRotateFile from 'winston-daily-rotate-file'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { BrowserWindow } from 'electron'
|
import os from 'os'
|
||||||
|
import { app, BrowserWindow } from 'electron'
|
||||||
import { serializeError, sanitizeError } from './error-utils'
|
import { serializeError, sanitizeError } from './error-utils'
|
||||||
import { getLogDir, isProduction, cleanupOldScreenshots } from './shared'
|
import { getLogDir, isProduction, cleanupOldScreenshots } from './shared'
|
||||||
import { IPC_CHANNELS } from '../../../shared/ipc-channels'
|
import { IPC_CHANNELS } from '../../../shared/ipc-channels'
|
||||||
import { getContext, run } from './request-context'
|
import { getContext, run } from './request-context'
|
||||||
|
import { createSeqTransportSync } from './seq-transport'
|
||||||
|
import type { SeqConfig } from '../../types/config.schema'
|
||||||
|
|
||||||
// Cache isProduction() at module load — app.isPackaged never changes at runtime
|
// Cache isProduction() at module load — app.isPackaged never changes at runtime
|
||||||
const IS_PROD = isProduction()
|
const IS_PROD = isProduction()
|
||||||
@@ -154,7 +157,11 @@ const createFileTransport = (level?: string, maxFiles?: string): DailyRotateFile
|
|||||||
// File transports are added after config is loaded via applyLoggingConfig()
|
// File transports are added after config is loaded via applyLoggingConfig()
|
||||||
const logger = winston.createLogger({
|
const logger = winston.createLogger({
|
||||||
level: 'info', // Default level, can be updated via setLogLevel()
|
level: 'info', // Default level, can be updated via setLogLevel()
|
||||||
defaultMeta: { service: 'erpauto' },
|
defaultMeta: {
|
||||||
|
service: 'erpauto',
|
||||||
|
appVersion: app.getVersion(),
|
||||||
|
computerName: os.hostname()
|
||||||
|
},
|
||||||
transports: [
|
transports: [
|
||||||
// Console transport - always enabled
|
// Console transport - always enabled
|
||||||
new winston.transports.Console({
|
new winston.transports.Console({
|
||||||
@@ -181,10 +188,15 @@ export function setLogLevel(level: string): void {
|
|||||||
/**
|
/**
|
||||||
* Apply logging configuration from config file
|
* Apply logging configuration from config file
|
||||||
* Removes existing DailyRotateFile transports and recreates them with config values
|
* Removes existing DailyRotateFile transports and recreates them with config values
|
||||||
|
* Also configures Seq transport if enabled
|
||||||
*
|
*
|
||||||
* @param config - Logging configuration from config.yaml
|
* @param config - Logging configuration from config.yaml
|
||||||
|
* @param seqConfig - Optional Seq configuration from config.yaml
|
||||||
*/
|
*/
|
||||||
export function applyLoggingConfig(config: { level: string; appRetention: number }): void {
|
export function applyLoggingConfig(
|
||||||
|
config: { level: string; appRetention: number },
|
||||||
|
seqConfig?: SeqConfig
|
||||||
|
): void {
|
||||||
// Update log level
|
// Update log level
|
||||||
setLogLevel(config.level)
|
setLogLevel(config.level)
|
||||||
|
|
||||||
@@ -213,6 +225,47 @@ export function applyLoggingConfig(config: { level: string; appRetention: number
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add Seq transport if configured and enabled
|
||||||
|
// Note: Uses sync wrapper which initializes asynchronously
|
||||||
|
if (seqConfig && seqConfig.enabled && seqConfig.serverUrl) {
|
||||||
|
try {
|
||||||
|
const seqTransport = createSeqTransportSync(seqConfig)
|
||||||
|
if (seqTransport) {
|
||||||
|
logger.add(seqTransport)
|
||||||
|
logger.info('Seq transport added successfully', {
|
||||||
|
serverUrl: seqConfig.serverUrl,
|
||||||
|
batchPostingLimit: seqConfig.batchPostingLimit,
|
||||||
|
period: seqConfig.period,
|
||||||
|
context: 'seq-transport'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// Transport not ready yet, will be initialized on next call
|
||||||
|
logger.debug('Seq transport initialization in progress', {
|
||||||
|
serverUrl: seqConfig.serverUrl,
|
||||||
|
context: 'seq-transport'
|
||||||
|
})
|
||||||
|
// Initialize and add when ready
|
||||||
|
import('./seq-transport').then(({ createSeqTransport }) => {
|
||||||
|
createSeqTransport(seqConfig).then((transport) => {
|
||||||
|
if (transport) {
|
||||||
|
logger.add(transport)
|
||||||
|
logger.info('Seq transport added (async init complete)', {
|
||||||
|
serverUrl: seqConfig.serverUrl,
|
||||||
|
context: 'seq-transport'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Failed to add Seq transport', {
|
||||||
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
context: 'seq-transport'
|
||||||
|
})
|
||||||
|
// Don't throw - allow app to continue without Seq
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Clean up old screenshot files beyond the retention window
|
// Clean up old screenshot files beyond the retention window
|
||||||
cleanupOldScreenshots(config.appRetention)
|
cleanupOldScreenshots(config.appRetention)
|
||||||
}
|
}
|
||||||
|
|||||||
131
src/main/services/logger/seq-transport.ts
Normal file
131
src/main/services/logger/seq-transport.ts
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
/**
|
||||||
|
* Seq logging transport using @datalust/winston-seq
|
||||||
|
*
|
||||||
|
* Provides asynchronous batched logging to Seq server
|
||||||
|
* with configurable parameters for production use.
|
||||||
|
*
|
||||||
|
* Features:
|
||||||
|
* - Non-blocking async batch sending (via dynamic import)
|
||||||
|
* - Configurable batch size, period, queue limits
|
||||||
|
* - Automatic retry on failure
|
||||||
|
* - Graceful shutdown with flush
|
||||||
|
* - Error suppression (Seq failures don't crash app)
|
||||||
|
*
|
||||||
|
* Note: Uses dynamic import because @datalust/winston-seq is an ESM module
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { SeqConfig } from '../../types/config.schema'
|
||||||
|
import logger from './index'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Seq transport constructor type (from dynamic import)
|
||||||
|
*/
|
||||||
|
type SeqTransportClass = new (options: {
|
||||||
|
serverUrl: string
|
||||||
|
apiKey?: string
|
||||||
|
batchSizeLimit?: number
|
||||||
|
maxBatchingTime?: number
|
||||||
|
maxRetries?: number
|
||||||
|
onError?: (error: Error) => void
|
||||||
|
}) => any
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and configure Seq transport dynamically
|
||||||
|
*
|
||||||
|
* This function handles the ESM module loading and returns
|
||||||
|
* a configured transport instance compatible with Winston.
|
||||||
|
*
|
||||||
|
* @param config - Seq configuration from config.schema
|
||||||
|
* @returns Promise resolving to transport instance or null if disabled/failed
|
||||||
|
*/
|
||||||
|
export async function createSeqTransport(config: SeqConfig): Promise<any> {
|
||||||
|
// Don't create transport if disabled
|
||||||
|
if (!config.enabled || !config.serverUrl) {
|
||||||
|
logger.info('Seq logging is disabled or server URL not provided', {
|
||||||
|
enabled: config.enabled,
|
||||||
|
hasServerUrl: !!config.serverUrl,
|
||||||
|
context: 'seq-transport'
|
||||||
|
})
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Dynamically import the ESM module
|
||||||
|
const { SeqTransport } = await import('@datalust/winston-seq')
|
||||||
|
const SeqTransportClass = SeqTransport as SeqTransportClass
|
||||||
|
|
||||||
|
// Create transport instance with mapped config
|
||||||
|
const transport = new SeqTransportClass({
|
||||||
|
serverUrl: config.serverUrl,
|
||||||
|
apiKey: config.apiKey || undefined,
|
||||||
|
batchSizeLimit: config.batchPostingLimit,
|
||||||
|
maxBatchingTime: config.period,
|
||||||
|
maxRetries: config.maxRetries,
|
||||||
|
onError: (error: Error) => {
|
||||||
|
// Log errors but don't throw - Seq failures should not crash app
|
||||||
|
logger.error('Seq transport error', {
|
||||||
|
error: error.message,
|
||||||
|
stack: error.stack,
|
||||||
|
context: 'seq-transport'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
logger.info('Seq transport created successfully', {
|
||||||
|
serverUrl: config.serverUrl,
|
||||||
|
batchPostingLimit: config.batchPostingLimit,
|
||||||
|
period: config.period,
|
||||||
|
maxRetries: config.maxRetries,
|
||||||
|
context: 'seq-transport'
|
||||||
|
})
|
||||||
|
|
||||||
|
return transport
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Failed to create Seq transport', {
|
||||||
|
error: error instanceof Error ? error.message : String(error),
|
||||||
|
context: 'seq-transport'
|
||||||
|
})
|
||||||
|
// Return null to allow app to continue without Seq
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sync factory wrapper for backward compatibility
|
||||||
|
*
|
||||||
|
* Note: This creates the transport asynchronously internally.
|
||||||
|
* The transport is cached and reused.
|
||||||
|
*/
|
||||||
|
let cachedTransport: any = null
|
||||||
|
let isInitializing = false
|
||||||
|
|
||||||
|
export function createSeqTransportSync(config: SeqConfig): any {
|
||||||
|
if (!config.enabled || !config.serverUrl) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cachedTransport) {
|
||||||
|
return cachedTransport
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isInitializing) {
|
||||||
|
isInitializing = true
|
||||||
|
createSeqTransport(config)
|
||||||
|
.then((transport) => {
|
||||||
|
cachedTransport = transport
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
logger.error('Seq transport initialization failed', {
|
||||||
|
error: error instanceof Error ? error.message : String(error)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
isInitializing = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return null on first call, transport will be available on next call
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export default createSeqTransportSync
|
||||||
@@ -35,6 +35,8 @@ export interface AuditEntry {
|
|||||||
username: string
|
username: string
|
||||||
/** Computer name where action was performed */
|
/** Computer name where action was performed */
|
||||||
computerName: string
|
computerName: string
|
||||||
|
/** Application version when action was performed */
|
||||||
|
appVersion: string
|
||||||
/** Resource affected by the action */
|
/** Resource affected by the action */
|
||||||
resource?: string
|
resource?: string
|
||||||
/** Status of the action */
|
/** Status of the action */
|
||||||
|
|||||||
@@ -132,6 +132,21 @@ export const loggingConfigSchema = z.object({
|
|||||||
appRetention: z.number().int().min(1).max(365).default(14)
|
appRetention: z.number().int().min(1).max(365).default(14)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Seq 日志聚合服务配置 Schema
|
||||||
|
*/
|
||||||
|
export const seqConfigSchema = z.object({
|
||||||
|
enabled: z.boolean().default(false),
|
||||||
|
serverUrl: z.string().url('Seq server URL must be a valid URL').optional().default(''),
|
||||||
|
apiKey: z.string().optional().default(''),
|
||||||
|
batchPostingLimit: z.number().int().min(1).max(1000).default(50),
|
||||||
|
period: z.number().int().min(1000).max(30000).default(2000), // milliseconds
|
||||||
|
queueLimit: z.number().int().min(1).max(10000).default(10000),
|
||||||
|
maxRetries: z.number().int().min(0).max(10).default(3)
|
||||||
|
})
|
||||||
|
|
||||||
|
export type SeqConfig = z.infer<typeof seqConfigSchema>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RustFS 对象存储配置 Schema
|
* RustFS 对象存储配置 Schema
|
||||||
*/
|
*/
|
||||||
@@ -172,6 +187,7 @@ export const fullConfigSchema = z.object({
|
|||||||
cleaner: cleanerConfigSchema,
|
cleaner: cleanerConfigSchema,
|
||||||
orderResolution: orderResolutionSchema,
|
orderResolution: orderResolutionSchema,
|
||||||
logging: loggingConfigSchema,
|
logging: loggingConfigSchema,
|
||||||
|
seq: seqConfigSchema.optional(),
|
||||||
rustfs: rustfsConfigSchema.optional(),
|
rustfs: rustfsConfigSchema.optional(),
|
||||||
update: updateConfigSchema.optional()
|
update: updateConfigSchema.optional()
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user