From 07bef74dbdcc5686720adbe0f85e18a193083d3e Mon Sep 17 00:00:00 2001 From: "Anthony D. Mays" Date: Tue, 14 Jan 2025 23:08:16 -0800 Subject: [PATCH 1/7] chore: adds basic logger Signed-off-by: Anthony D. Mays --- .../fullstack_demo/package-lock.json | 276 +++++++++++++++++- lib/javascript/fullstack_demo/package.json | 6 +- .../fullstack_demo/src/app/api/log/route.ts | 7 + lib/javascript/fullstack_demo/src/logger.ts | 4 + .../fullstack_demo/src/middleware.ts | 10 + .../fullstack_demo/src/util/client-logger.ts | 25 ++ .../fullstack_demo/src/util/logger.ts | 8 + .../fullstack_demo/src/util/server-logger.ts | 50 ++++ 8 files changed, 372 insertions(+), 14 deletions(-) create mode 100644 lib/javascript/fullstack_demo/src/app/api/log/route.ts create mode 100644 lib/javascript/fullstack_demo/src/logger.ts create mode 100644 lib/javascript/fullstack_demo/src/util/client-logger.ts create mode 100644 lib/javascript/fullstack_demo/src/util/logger.ts create mode 100644 lib/javascript/fullstack_demo/src/util/server-logger.ts diff --git a/lib/javascript/fullstack_demo/package-lock.json b/lib/javascript/fullstack_demo/package-lock.json index 01069ed95..84a50fa5f 100644 --- a/lib/javascript/fullstack_demo/package-lock.json +++ b/lib/javascript/fullstack_demo/package-lock.json @@ -14,7 +14,9 @@ "@upstash/redis": "^1.34.3", "lowdb": "^7.0.1", "lucide-react": "^0.468.0", - "next": "^14.2.22" + "next": "^14.2.22", + "uuid": "^11.0.5", + "winston": "^3.17.0" }, "devDependencies": { "@testing-library/dom": "^10.4.0", @@ -553,6 +555,16 @@ "node": ">= 6" } }, + "node_modules/@cypress/request/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@cypress/xvfb": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", @@ -574,6 +586,17 @@ "ms": "^2.1.1" } }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "license": "MIT", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", @@ -2013,6 +2036,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "license": "MIT" + }, "node_modules/@types/yauzl": { "version": "2.10.3", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", @@ -2862,7 +2891,6 @@ "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "dev": true, "license": "MIT" }, "node_modules/asynckit": { @@ -3404,6 +3432,16 @@ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "license": "MIT" }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -3421,7 +3459,31 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "license": "MIT" }, "node_modules/colorette": { @@ -3431,6 +3493,16 @@ "dev": true, "license": "MIT" }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "license": "MIT", + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -3907,6 +3979,12 @@ "dev": true, "license": "MIT" }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "license": "MIT" + }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -4895,6 +4973,12 @@ "pend": "~1.2.0" } }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "license": "MIT" + }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -4985,6 +5069,12 @@ "dev": true, "license": "ISC" }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "license": "MIT" + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -5591,6 +5681,12 @@ "node": ">=8" } }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, "node_modules/ini": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", @@ -5633,6 +5729,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "license": "MIT" + }, "node_modules/is-async-function": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", @@ -5964,7 +6066,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -6411,6 +6512,12 @@ "json-buffer": "3.0.1" } }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "license": "MIT" + }, "node_modules/language-subtag-registry": { "version": "0.3.23", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", @@ -6671,6 +6778,32 @@ "node": ">=8" } }, + "node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "license": "MIT", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/logform/node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -6891,7 +7024,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/mz": { @@ -7195,6 +7327,15 @@ "wrappy": "1" } }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "license": "MIT", + "dependencies": { + "fn.name": "1.x.x" + } + }, "node_modules/onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", @@ -7847,6 +7988,20 @@ "pify": "^2.3.0" } }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -8098,7 +8253,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -8133,6 +8287,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -8287,6 +8450,15 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, "node_modules/slice-ansi": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", @@ -8361,6 +8533,15 @@ "node": ">=0.10.0" } }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -8394,6 +8575,15 @@ "node": ">=10.0.0" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -8840,6 +9030,12 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "license": "MIT" + }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", @@ -9003,6 +9199,15 @@ "tree-kill": "cli.js" } }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/truncate-utf8-bytes": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", @@ -9314,17 +9519,19 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, "license": "MIT" }, "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.5.tgz", + "integrity": "sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "license": "MIT", "bin": { - "uuid": "dist/bin/uuid" + "uuid": "dist/esm/bin/uuid" } }, "node_modules/verror": { @@ -9701,6 +9908,51 @@ "node": ">=8" } }, + "node_modules/winston": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", + "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", + "license": "MIT", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "license": "MIT", + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston/node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", diff --git a/lib/javascript/fullstack_demo/package.json b/lib/javascript/fullstack_demo/package.json index 2935d5214..8cf246b2b 100644 --- a/lib/javascript/fullstack_demo/package.json +++ b/lib/javascript/fullstack_demo/package.json @@ -19,7 +19,9 @@ "@upstash/redis": "^1.34.3", "lowdb": "^7.0.1", "lucide-react": "^0.468.0", - "next": "^14.2.22" + "next": "^14.2.22", + "uuid": "^11.0.5", + "winston": "^3.17.0" }, "devDependencies": { "@testing-library/dom": "^10.4.0", @@ -46,4 +48,4 @@ "vitest": "^2.1.8", "vitest-fetch-mock": "^0.4.3" } -} \ No newline at end of file +} diff --git a/lib/javascript/fullstack_demo/src/app/api/log/route.ts b/lib/javascript/fullstack_demo/src/app/api/log/route.ts new file mode 100644 index 000000000..cb9d8e21a --- /dev/null +++ b/lib/javascript/fullstack_demo/src/app/api/log/route.ts @@ -0,0 +1,7 @@ +import { logger } from '@/util/server-logger'; + +export async function POST(request: Request) { + const { level, message, vars } = await request.json(); + logger.log(level, message, ...(vars || [])); + return new Response(null, { status: 204 }); +} diff --git a/lib/javascript/fullstack_demo/src/logger.ts b/lib/javascript/fullstack_demo/src/logger.ts new file mode 100644 index 000000000..53b967f7d --- /dev/null +++ b/lib/javascript/fullstack_demo/src/logger.ts @@ -0,0 +1,4 @@ +import { Logger } from '@/util/logger'; +import { WinstonLogger } from '@/util/winston-logger'; + +export const logger: Logger = new WinstonLogger(); diff --git a/lib/javascript/fullstack_demo/src/middleware.ts b/lib/javascript/fullstack_demo/src/middleware.ts index 63f276baf..80a2bc2c8 100644 --- a/lib/javascript/fullstack_demo/src/middleware.ts +++ b/lib/javascript/fullstack_demo/src/middleware.ts @@ -1,9 +1,19 @@ import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'; +import { NextResponse } from 'next/server'; +import { v4 as uuidv4 } from 'uuid'; +const CORRELATION_ID_HEADER = 'x-correlation-id'; const isProtectedRoute = createRouteMatcher(['/(.*)']); export default clerkMiddleware(async (auth, req) => { + const correlationId = uuidv4(); + req.headers.set(CORRELATION_ID_HEADER, correlationId); + if (isProtectedRoute(req)) await auth.protect(); + + const response = NextResponse.next(); + response.headers.set(CORRELATION_ID_HEADER, correlationId); + return response; }); export const config = { diff --git a/lib/javascript/fullstack_demo/src/util/client-logger.ts b/lib/javascript/fullstack_demo/src/util/client-logger.ts new file mode 100644 index 000000000..dc04511b0 --- /dev/null +++ b/lib/javascript/fullstack_demo/src/util/client-logger.ts @@ -0,0 +1,25 @@ +import { Logger, LogLevel } from './logger'; + +class ClientLogger implements Logger { + log(level: LogLevel, message: string, ...vars: unknown[]): void { + fetch('/api/log', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ level, message, vars }), + }); + } + + debug(format: string, ...vars: unknown[]): void { + this.log('DEBUG', format, ...vars); + } + info(format: string, ...vars: unknown[]): void { + this.log('INFO', format, ...vars); + } + error(format: string, ...vars: unknown[]): void { + this.log('ERROR', format, ...vars); + } +} + +export const logger = new ClientLogger(); diff --git a/lib/javascript/fullstack_demo/src/util/logger.ts b/lib/javascript/fullstack_demo/src/util/logger.ts new file mode 100644 index 000000000..0aca50863 --- /dev/null +++ b/lib/javascript/fullstack_demo/src/util/logger.ts @@ -0,0 +1,8 @@ +export interface Logger { + log(level: LogLevel, message: string, ...vars: unknown[]): void; + debug(format: string, ...vars: unknown[]): void; + info(format: string, ...vars: unknown[]): void; + error(format: string, ...vars: unknown[]): void; +} + +export type LogLevel = 'DEBUG' | 'INFO' | 'ERROR' | 'WARN'; diff --git a/lib/javascript/fullstack_demo/src/util/server-logger.ts b/lib/javascript/fullstack_demo/src/util/server-logger.ts new file mode 100644 index 000000000..e1949f362 --- /dev/null +++ b/lib/javascript/fullstack_demo/src/util/server-logger.ts @@ -0,0 +1,50 @@ +import { auth } from '@clerk/nextjs/server'; +import { headers } from 'next/headers'; +import winston, { format } from 'winston'; +import { Logger, LogLevel } from './logger'; +const { combine, timestamp, json } = format; + +export class WinstonLogger implements Logger { + private readonly winstonLogger: winston.Logger; + + constructor() { + this.winstonLogger = winston.createLogger({ + level: 'info', + format: combine( + format((info) => { + const headerList = headers(); + info.correlationId = headerList.get('x-correlation-id'); + return info; + })(), + timestamp(), + json(), + ), + transports: [ + new winston.transports.Console(), + new winston.transports.File({ filename: 'debug.log', level: 'debug' }), + ], + }); + } + + async log(level: LogLevel, message: string, ...vars: unknown[]) { + const { userId } = await auth(); + this.winstonLogger.log(level.toLowerCase(), message, { ...vars, userId }); + } + + async debug(format: string, ...vars: unknown[]) { + const { userId } = await auth(); + this.winstonLogger.debug(format, { ...vars, userId }); + } + + async info(format: string, ...vars: unknown[]) { + const { userId } = await auth(); + this.winstonLogger.info(format, { ...vars, userId }); + } + + async error(format: string, ...vars: unknown[]) { + const { userId } = await auth(); + this.winstonLogger.error(format, { ...vars, userId }); + } +} + +export const logger = new WinstonLogger(); From fbfc8163ec40d8fa4987399b2010ed57edee6d4b Mon Sep 17 00:00:00 2001 From: "Anthony D. Mays" Date: Wed, 15 Jan 2025 09:14:36 -0800 Subject: [PATCH 2/7] chore: configures batching for client logger --- .../fullstack_demo/src/app/api/log/route.ts | 7 +- lib/javascript/fullstack_demo/src/models.ts | 8 ++ .../fullstack_demo/src/util/client-logger.ts | 82 +++++++++++++++++-- .../fullstack_demo/src/util/logger.ts | 10 +-- .../fullstack_demo/src/util/server-logger.ts | 12 +-- 5 files changed, 97 insertions(+), 22 deletions(-) diff --git a/lib/javascript/fullstack_demo/src/app/api/log/route.ts b/lib/javascript/fullstack_demo/src/app/api/log/route.ts index cb9d8e21a..f55a2c109 100644 --- a/lib/javascript/fullstack_demo/src/app/api/log/route.ts +++ b/lib/javascript/fullstack_demo/src/app/api/log/route.ts @@ -1,7 +1,10 @@ import { logger } from '@/util/server-logger'; export async function POST(request: Request) { - const { level, message, vars } = await request.json(); - logger.log(level, message, ...(vars || [])); + const batch = await request.json(); + for (const log of batch) { + const { level, message, vars } = log; + logger.log(level, message, { ...(vars || {}) }); + } return new Response(null, { status: 204 }); } diff --git a/lib/javascript/fullstack_demo/src/models.ts b/lib/javascript/fullstack_demo/src/models.ts index 6516bf8b9..99dcdb680 100644 --- a/lib/javascript/fullstack_demo/src/models.ts +++ b/lib/javascript/fullstack_demo/src/models.ts @@ -1,5 +1,13 @@ +import { LogLevel } from './util/logger'; + export interface Todo { id: number; text: string; completed: boolean; } + +export interface Log { + level: LogLevel; + message: string; + vars: {}; +} diff --git a/lib/javascript/fullstack_demo/src/util/client-logger.ts b/lib/javascript/fullstack_demo/src/util/client-logger.ts index dc04511b0..c76e0dbde 100644 --- a/lib/javascript/fullstack_demo/src/util/client-logger.ts +++ b/lib/javascript/fullstack_demo/src/util/client-logger.ts @@ -1,24 +1,88 @@ +import { Log } from '@/models'; import { Logger, LogLevel } from './logger'; +const FLUSH_AFTER_SIZE = 15; +const MAX_BATCH_SIZE = 100; +const FLUSH_INTERVAL_MS = 1000 * 5; // 5 seconds + class ClientLogger implements Logger { - log(level: LogLevel, message: string, ...vars: unknown[]): void { - fetch('/api/log', { + private readonly buffer: Log[] = []; + private flushing = false; + + constructor() { + setInterval(() => this.flush(), FLUSH_INTERVAL_MS); + } + + log(level: LogLevel, message: string, vars?: {}): void { + vars = { ...this.getDefaultVars(), ...(vars || {}) }; + this.buffer.push({ level, message, vars }); + if (this.buffer.length >= FLUSH_AFTER_SIZE && !this.flushing) { + this.flush(); + } + } + + private getDefaultVars() { + if (typeof window === 'undefined') { + return []; + } + + return { + client: { + type: 'web', + userAgent: navigator.userAgent, + location: window.location.href, + }, + }; + } + + async flush(): Promise { + if (this.buffer.length === 0 || this.flushing) { + return; + } + + this.flushing = true; + + const batch = this.buffer.splice(0, MAX_BATCH_SIZE); + let backoffInMs = FLUSH_INTERVAL_MS; + + do { + try { + await this.sendLogs(batch); + this.flushing = false; + } catch (e) { + console.error('Failed to send logs', e); + backoffInMs *= 2; + await this.delay(backoffInMs); + } + } while (this.flushing); + } + + async sendLogs(batch: Log[]): Promise { + let endpoint = '/api/log'; + if (typeof window === 'undefined') { + endpoint = `${process.env?.NEXT_PUBLIC_API_URL || ''}/api/host`; + } + await fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ level, message, vars }), + body: JSON.stringify(batch), }); } - debug(format: string, ...vars: unknown[]): void { - this.log('DEBUG', format, ...vars); + async delay(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)); + } + + debug(format: string, vars?: {}): void { + this.log('debug', format, vars); } - info(format: string, ...vars: unknown[]): void { - this.log('INFO', format, ...vars); + info(format: string, vars?: {}): void { + this.log('info', format, vars); } - error(format: string, ...vars: unknown[]): void { - this.log('ERROR', format, ...vars); + error(format: string, vars?: {}): void { + this.log('error', format, vars); } } diff --git a/lib/javascript/fullstack_demo/src/util/logger.ts b/lib/javascript/fullstack_demo/src/util/logger.ts index 0aca50863..93fb7178e 100644 --- a/lib/javascript/fullstack_demo/src/util/logger.ts +++ b/lib/javascript/fullstack_demo/src/util/logger.ts @@ -1,8 +1,8 @@ export interface Logger { - log(level: LogLevel, message: string, ...vars: unknown[]): void; - debug(format: string, ...vars: unknown[]): void; - info(format: string, ...vars: unknown[]): void; - error(format: string, ...vars: unknown[]): void; + log(level: LogLevel, message: string, vars?: {}): void; + debug(format: string, vars?: {}): void; + info(format: string, vars?: {}): void; + error(format: string, vars?: {}): void; } -export type LogLevel = 'DEBUG' | 'INFO' | 'ERROR' | 'WARN'; +export type LogLevel = 'debug' | 'info' | 'error' | 'warn'; diff --git a/lib/javascript/fullstack_demo/src/util/server-logger.ts b/lib/javascript/fullstack_demo/src/util/server-logger.ts index e1949f362..616cf937c 100644 --- a/lib/javascript/fullstack_demo/src/util/server-logger.ts +++ b/lib/javascript/fullstack_demo/src/util/server-logger.ts @@ -9,7 +9,7 @@ export class WinstonLogger implements Logger { constructor() { this.winstonLogger = winston.createLogger({ - level: 'info', + level: 'debug', format: combine( format((info) => { const headerList = headers(); @@ -20,28 +20,28 @@ export class WinstonLogger implements Logger { json(), ), transports: [ - new winston.transports.Console(), + new winston.transports.Console({ level: 'info' }), new winston.transports.File({ filename: 'debug.log', level: 'debug' }), ], }); } - async log(level: LogLevel, message: string, ...vars: unknown[]) { + async log(level: LogLevel, message: string, vars: {}) { const { userId } = await auth(); this.winstonLogger.log(level.toLowerCase(), message, { ...vars, userId }); } - async debug(format: string, ...vars: unknown[]) { + async debug(format: string, vars: {}) { const { userId } = await auth(); this.winstonLogger.debug(format, { ...vars, userId }); } - async info(format: string, ...vars: unknown[]) { + async info(format: string, vars: {}) { const { userId } = await auth(); this.winstonLogger.info(format, { ...vars, userId }); } - async error(format: string, ...vars: unknown[]) { + async error(format: string, vars: {}) { const { userId } = await auth(); this.winstonLogger.error(format, { ...vars, userId }); } From c04725daf88b76f1c00573bfe4837aab78fe1565 Mon Sep 17 00:00:00 2001 From: "Anthony D. Mays" Date: Wed, 15 Jan 2025 09:55:39 -0800 Subject: [PATCH 3/7] wip: attempts to add a prisma transport for logger --- .../fullstack_demo/prisma/schema.prisma | 9 +++++++ .../src/components/todo-list/todo-list.tsx | 4 +++ lib/javascript/fullstack_demo/src/logger.ts | 4 --- .../fullstack_demo/src/util/client-logger.ts | 12 ++++++--- .../fullstack_demo/src/util/events.ts | 5 ++++ .../fullstack_demo/src/util/logger.ts | 3 +++ .../src/util/prisma-transport.ts | 27 +++++++++++++++++++ .../fullstack_demo/src/util/server-logger.ts | 16 ++++++++--- 8 files changed, 68 insertions(+), 12 deletions(-) delete mode 100644 lib/javascript/fullstack_demo/src/logger.ts create mode 100644 lib/javascript/fullstack_demo/src/util/events.ts create mode 100644 lib/javascript/fullstack_demo/src/util/prisma-transport.ts diff --git a/lib/javascript/fullstack_demo/prisma/schema.prisma b/lib/javascript/fullstack_demo/prisma/schema.prisma index fd5de8a57..c3eb4add3 100644 --- a/lib/javascript/fullstack_demo/prisma/schema.prisma +++ b/lib/javascript/fullstack_demo/prisma/schema.prisma @@ -18,3 +18,12 @@ model Todos { created_at DateTime @default(now()) @db.Timestamptz(6) updated_at DateTime @default(now()) @db.Timestamp(6) } + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model Logs { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + level String @db.VarChar + message String @default("") @db.VarChar + timestamp DateTime @db.Timestamp(6) + meta Json? @db.Json +} diff --git a/lib/javascript/fullstack_demo/src/components/todo-list/todo-list.tsx b/lib/javascript/fullstack_demo/src/components/todo-list/todo-list.tsx index f7cd6d938..f34dbba05 100644 --- a/lib/javascript/fullstack_demo/src/components/todo-list/todo-list.tsx +++ b/lib/javascript/fullstack_demo/src/components/todo-list/todo-list.tsx @@ -1,4 +1,6 @@ import { Todo } from '@/models'; +import { logger } from '@/util/client-logger'; +import { LogEvent } from '@/util/events'; import { TodoComponent } from './todo'; export type TodoProps = { @@ -8,6 +10,7 @@ export type TodoProps = { export const TodoList: React.FC = ({ todos, onChange }) => { const toggleTodo = async (id: number) => { + logger.event(LogEvent.TODO_TOGGLE, { id }); const todo = todos.find((todo) => todo.id === id); if (!todo) return; todo.completed = !todo.completed; @@ -21,6 +24,7 @@ export const TodoList: React.FC = ({ todos, onChange }) => { }; const deleteTodo = async (id: number) => { + logger.event(LogEvent.TODO_DELETE, { id }); await fetch(`/api/todos/${id}`, { method: 'DELETE', }); diff --git a/lib/javascript/fullstack_demo/src/logger.ts b/lib/javascript/fullstack_demo/src/logger.ts deleted file mode 100644 index 53b967f7d..000000000 --- a/lib/javascript/fullstack_demo/src/logger.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Logger } from '@/util/logger'; -import { WinstonLogger } from '@/util/winston-logger'; - -export const logger: Logger = new WinstonLogger(); diff --git a/lib/javascript/fullstack_demo/src/util/client-logger.ts b/lib/javascript/fullstack_demo/src/util/client-logger.ts index c76e0dbde..f013eb478 100644 --- a/lib/javascript/fullstack_demo/src/util/client-logger.ts +++ b/lib/javascript/fullstack_demo/src/util/client-logger.ts @@ -1,4 +1,5 @@ import { Log } from '@/models'; +import { LogEvent } from './events'; import { Logger, LogLevel } from './logger'; const FLUSH_AFTER_SIZE = 15; @@ -13,7 +14,7 @@ class ClientLogger implements Logger { setInterval(() => this.flush(), FLUSH_INTERVAL_MS); } - log(level: LogLevel, message: string, vars?: {}): void { + log(level: LogLevel, message: string, vars = {}): void { vars = { ...this.getDefaultVars(), ...(vars || {}) }; this.buffer.push({ level, message, vars }); if (this.buffer.length >= FLUSH_AFTER_SIZE && !this.flushing) { @@ -75,15 +76,18 @@ class ClientLogger implements Logger { return new Promise((resolve) => setTimeout(resolve, ms)); } - debug(format: string, vars?: {}): void { + debug(format: string, vars = {}): void { this.log('debug', format, vars); } - info(format: string, vars?: {}): void { + info(format: string, vars = {}): void { this.log('info', format, vars); } - error(format: string, vars?: {}): void { + error(format: string, vars = {}): void { this.log('error', format, vars); } + event(eventId: LogEvent, vars = {}): void { + this.log('info', '', { ...vars, eventId }); + } } export const logger = new ClientLogger(); diff --git a/lib/javascript/fullstack_demo/src/util/events.ts b/lib/javascript/fullstack_demo/src/util/events.ts new file mode 100644 index 000000000..8f0cd0573 --- /dev/null +++ b/lib/javascript/fullstack_demo/src/util/events.ts @@ -0,0 +1,5 @@ +export enum LogEvent { + TODO_TOGGLE = 'todo_toggle', + TODO_DELETE = 'todo_delete', + TODO_ADD = 'todo_add', +} diff --git a/lib/javascript/fullstack_demo/src/util/logger.ts b/lib/javascript/fullstack_demo/src/util/logger.ts index 93fb7178e..3a7fdc76d 100644 --- a/lib/javascript/fullstack_demo/src/util/logger.ts +++ b/lib/javascript/fullstack_demo/src/util/logger.ts @@ -1,8 +1,11 @@ +import { LogEvent } from './events'; + export interface Logger { log(level: LogLevel, message: string, vars?: {}): void; debug(format: string, vars?: {}): void; info(format: string, vars?: {}): void; error(format: string, vars?: {}): void; + event(id: LogEvent, vars?: {}): void; } export type LogLevel = 'debug' | 'info' | 'error' | 'warn'; diff --git a/lib/javascript/fullstack_demo/src/util/prisma-transport.ts b/lib/javascript/fullstack_demo/src/util/prisma-transport.ts new file mode 100644 index 000000000..d26a78924 --- /dev/null +++ b/lib/javascript/fullstack_demo/src/util/prisma-transport.ts @@ -0,0 +1,27 @@ +import { PrismaClient } from '@prisma/client'; +import Transport from 'winston-transport'; + +export class PrismaTransport extends Transport { + private readonly client = new PrismaClient(); + + constructor(opts: any) { + super(opts); + } + + log(info: any, callback: () => void): void { + setImmediate(() => { + this.emit('logged', info); + }); + + this.client.logs.create({ + data: { + level: info.level, + message: info.message, + meta: info.vars, + timestamp: info.timestamp, + }, + }); + + callback(); + } +} diff --git a/lib/javascript/fullstack_demo/src/util/server-logger.ts b/lib/javascript/fullstack_demo/src/util/server-logger.ts index 616cf937c..719883dd5 100644 --- a/lib/javascript/fullstack_demo/src/util/server-logger.ts +++ b/lib/javascript/fullstack_demo/src/util/server-logger.ts @@ -1,7 +1,9 @@ import { auth } from '@clerk/nextjs/server'; import { headers } from 'next/headers'; import winston, { format } from 'winston'; +import { LogEvent } from './events'; import { Logger, LogLevel } from './logger'; +import { PrismaTransport } from './prisma-transport'; const { combine, timestamp, json } = format; export class WinstonLogger implements Logger { @@ -22,29 +24,35 @@ export class WinstonLogger implements Logger { transports: [ new winston.transports.Console({ level: 'info' }), new winston.transports.File({ filename: 'debug.log', level: 'debug' }), + new PrismaTransport({ level: 'debug' }), ], }); } - async log(level: LogLevel, message: string, vars: {}) { + async log(level: LogLevel, message: string, vars = {}) { const { userId } = await auth(); this.winstonLogger.log(level.toLowerCase(), message, { ...vars, userId }); } - async debug(format: string, vars: {}) { + async debug(format: string, vars = {}) { const { userId } = await auth(); this.winstonLogger.debug(format, { ...vars, userId }); } - async info(format: string, vars: {}) { + async info(format: string, vars = {}) { const { userId } = await auth(); this.winstonLogger.info(format, { ...vars, userId }); } - async error(format: string, vars: {}) { + async error(format: string, vars = {}) { const { userId } = await auth(); this.winstonLogger.error(format, { ...vars, userId }); } + + async event(eventId: LogEvent, vars = {}) { + const { userId } = await auth(); + this.winstonLogger.debug('', { ...vars, userId, eventId }); + } } export const logger = new WinstonLogger(); From da52eb554292c223f1f974d86444f88bb88c748b Mon Sep 17 00:00:00 2001 From: "Anthony D. Mays" Date: Wed, 15 Jan 2025 11:16:46 -0800 Subject: [PATCH 4/7] chore: updates logs table to use jsonb --- .../fullstack_demo/prisma/schema.prisma | 2 +- .../src/util/prisma-transport.ts | 22 +++++++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/javascript/fullstack_demo/prisma/schema.prisma b/lib/javascript/fullstack_demo/prisma/schema.prisma index c3eb4add3..c67f6a70f 100644 --- a/lib/javascript/fullstack_demo/prisma/schema.prisma +++ b/lib/javascript/fullstack_demo/prisma/schema.prisma @@ -25,5 +25,5 @@ model Logs { level String @db.VarChar message String @default("") @db.VarChar timestamp DateTime @db.Timestamp(6) - meta Json? @db.Json + meta Json? } diff --git a/lib/javascript/fullstack_demo/src/util/prisma-transport.ts b/lib/javascript/fullstack_demo/src/util/prisma-transport.ts index d26a78924..f564d0668 100644 --- a/lib/javascript/fullstack_demo/src/util/prisma-transport.ts +++ b/lib/javascript/fullstack_demo/src/util/prisma-transport.ts @@ -8,19 +8,23 @@ export class PrismaTransport extends Transport { super(opts); } - log(info: any, callback: () => void): void { + async log(info: any, callback: () => void): Promise { setImmediate(() => { this.emit('logged', info); }); - this.client.logs.create({ - data: { - level: info.level, - message: info.message, - meta: info.vars, - timestamp: info.timestamp, - }, - }); + try { + await this.client.logs.create({ + data: { + level: info.level, + message: info.message, + meta: info, + timestamp: info.timestamp, + }, + }); + } catch (ex) { + console.error('Failed to log to Prisma', ex); + } callback(); } From e851de8449cbb5a3411a9748601fbec9fb998d77 Mon Sep 17 00:00:00 2001 From: "Anthony D. Mays" Date: Wed, 15 Jan 2025 17:45:42 -0800 Subject: [PATCH 5/7] chore: adds event beaconing for features --- .../fullstack_demo/src/components/add-todo/add-todo.tsx | 4 +++- .../fullstack_demo/src/components/todo-list/todo-list.tsx | 5 +++-- lib/javascript/fullstack_demo/src/models.ts | 8 ++++++++ lib/javascript/fullstack_demo/src/util/client-logger.ts | 3 +-- lib/javascript/fullstack_demo/src/util/events.ts | 5 ----- lib/javascript/fullstack_demo/src/util/logger.ts | 2 +- lib/javascript/fullstack_demo/src/util/server-logger.ts | 2 +- 7 files changed, 17 insertions(+), 12 deletions(-) delete mode 100644 lib/javascript/fullstack_demo/src/util/events.ts diff --git a/lib/javascript/fullstack_demo/src/components/add-todo/add-todo.tsx b/lib/javascript/fullstack_demo/src/components/add-todo/add-todo.tsx index 541114e41..df299207f 100644 --- a/lib/javascript/fullstack_demo/src/components/add-todo/add-todo.tsx +++ b/lib/javascript/fullstack_demo/src/components/add-todo/add-todo.tsx @@ -1,4 +1,5 @@ -import { Todo } from '@/models'; +import { LogEvent, Todo } from '@/models'; +import { logger } from '@/util/client-logger'; import { PlusIcon } from 'lucide-react'; import { useState } from 'react'; @@ -14,6 +15,7 @@ export const AddTodo: React.FC = ({ onAdd }) => { if (newTodo.trim() !== '') { const todo: Todo = { id: Date.now(), text: newTodo, completed: false }; await fetch('/api/todos', { method: 'POST', body: JSON.stringify(todo) }); + logger.event(LogEvent.TODO_ADD, { id: todo.id }); setNewTodo(''); if (onAdd) { onAdd(todo); diff --git a/lib/javascript/fullstack_demo/src/components/todo-list/todo-list.tsx b/lib/javascript/fullstack_demo/src/components/todo-list/todo-list.tsx index f34dbba05..9f4626e41 100644 --- a/lib/javascript/fullstack_demo/src/components/todo-list/todo-list.tsx +++ b/lib/javascript/fullstack_demo/src/components/todo-list/todo-list.tsx @@ -1,6 +1,5 @@ -import { Todo } from '@/models'; +import { LogEvent, Todo } from '@/models'; import { logger } from '@/util/client-logger'; -import { LogEvent } from '@/util/events'; import { TodoComponent } from './todo'; export type TodoProps = { @@ -18,6 +17,7 @@ export const TodoList: React.FC = ({ todos, onChange }) => { method: 'PATCH', body: JSON.stringify(todo), }); + logger.event(LogEvent.TODO_TOGGLE, { id }); if (onChange) { onChange(todo.id); } @@ -28,6 +28,7 @@ export const TodoList: React.FC = ({ todos, onChange }) => { await fetch(`/api/todos/${id}`, { method: 'DELETE', }); + logger.event(LogEvent.TODO_DELETE, { id }); if (onChange) { onChange(id); } diff --git a/lib/javascript/fullstack_demo/src/models.ts b/lib/javascript/fullstack_demo/src/models.ts index 99dcdb680..7f3ba56a2 100644 --- a/lib/javascript/fullstack_demo/src/models.ts +++ b/lib/javascript/fullstack_demo/src/models.ts @@ -11,3 +11,11 @@ export interface Log { message: string; vars: {}; } + +export enum LogEvent { + LOG_IN = 'log_in', + LOG_OUT = 'log_out', + TODO_TOGGLE = 'todo_toggle', + TODO_DELETE = 'todo_delete', + TODO_ADD = 'todo_add', +} diff --git a/lib/javascript/fullstack_demo/src/util/client-logger.ts b/lib/javascript/fullstack_demo/src/util/client-logger.ts index f013eb478..402b9b9c9 100644 --- a/lib/javascript/fullstack_demo/src/util/client-logger.ts +++ b/lib/javascript/fullstack_demo/src/util/client-logger.ts @@ -1,5 +1,4 @@ -import { Log } from '@/models'; -import { LogEvent } from './events'; +import { Log, LogEvent } from '@/models'; import { Logger, LogLevel } from './logger'; const FLUSH_AFTER_SIZE = 15; diff --git a/lib/javascript/fullstack_demo/src/util/events.ts b/lib/javascript/fullstack_demo/src/util/events.ts deleted file mode 100644 index 8f0cd0573..000000000 --- a/lib/javascript/fullstack_demo/src/util/events.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum LogEvent { - TODO_TOGGLE = 'todo_toggle', - TODO_DELETE = 'todo_delete', - TODO_ADD = 'todo_add', -} diff --git a/lib/javascript/fullstack_demo/src/util/logger.ts b/lib/javascript/fullstack_demo/src/util/logger.ts index 3a7fdc76d..86d093267 100644 --- a/lib/javascript/fullstack_demo/src/util/logger.ts +++ b/lib/javascript/fullstack_demo/src/util/logger.ts @@ -1,4 +1,4 @@ -import { LogEvent } from './events'; +import { LogEvent } from '@/models'; export interface Logger { log(level: LogLevel, message: string, vars?: {}): void; diff --git a/lib/javascript/fullstack_demo/src/util/server-logger.ts b/lib/javascript/fullstack_demo/src/util/server-logger.ts index 719883dd5..0e8666e60 100644 --- a/lib/javascript/fullstack_demo/src/util/server-logger.ts +++ b/lib/javascript/fullstack_demo/src/util/server-logger.ts @@ -1,7 +1,7 @@ +import { LogEvent } from '@/models'; import { auth } from '@clerk/nextjs/server'; import { headers } from 'next/headers'; import winston, { format } from 'winston'; -import { LogEvent } from './events'; import { Logger, LogLevel } from './logger'; import { PrismaTransport } from './prisma-transport'; const { combine, timestamp, json } = format; From 252f5a4e9faab859cc381ec1047ab11f0e56ac2d Mon Sep 17 00:00:00 2001 From: "Anthony D. Mays" Date: Wed, 15 Jan 2025 17:49:05 -0800 Subject: [PATCH 6/7] chore: adds environment to logger config --- lib/javascript/fullstack_demo/src/util/server-logger.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/javascript/fullstack_demo/src/util/server-logger.ts b/lib/javascript/fullstack_demo/src/util/server-logger.ts index 0e8666e60..12f18ff41 100644 --- a/lib/javascript/fullstack_demo/src/util/server-logger.ts +++ b/lib/javascript/fullstack_demo/src/util/server-logger.ts @@ -16,6 +16,7 @@ export class WinstonLogger implements Logger { format((info) => { const headerList = headers(); info.correlationId = headerList.get('x-correlation-id'); + info.environment = process.env.NODE_ENV; return info; })(), timestamp(), From 9e640f640dfa0b87ff4ab1759ccf9a2285dbff8d Mon Sep 17 00:00:00 2001 From: "Anthony D. Mays" Date: Wed, 15 Jan 2025 17:50:08 -0800 Subject: [PATCH 7/7] chore: removes file logger from default config --- lib/javascript/fullstack_demo/src/util/server-logger.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/javascript/fullstack_demo/src/util/server-logger.ts b/lib/javascript/fullstack_demo/src/util/server-logger.ts index 12f18ff41..c088d6975 100644 --- a/lib/javascript/fullstack_demo/src/util/server-logger.ts +++ b/lib/javascript/fullstack_demo/src/util/server-logger.ts @@ -24,7 +24,6 @@ export class WinstonLogger implements Logger { ), transports: [ new winston.transports.Console({ level: 'info' }), - new winston.transports.File({ filename: 'debug.log', level: 'debug' }), new PrismaTransport({ level: 'debug' }), ], });