{"_id":"@dylannormandin/logger","_rev":"4-1696fc49076952d8913c671ad619a494","name":"@dylannormandin/logger","dist-tags":{"latest":"0.1.0"},"versions":{"0.0.0":{"name":"@dylannormandin/logger","version":"0.0.0","keywords":[],"author":"","license":"ISC","_id":"@dylannormandin/logger@0.0.0","maintainers":[{"name":"dylannormandin","email":"dtrnormandin@gmail.com"}],"homepage":"https://github.com/DylanNormandin/custom-packages#readme","bugs":{"url":"https://github.com/DylanNormandin/custom-packages/issues"},"dist":{"shasum":"680bbb24201e13d83b2f0964e81cee61f81d3b43","tarball":"https://registry.npmjs.org/@dylannormandin/logger/-/logger-0.0.0.tgz","fileCount":5,"integrity":"sha512-gzzceU1ll6ttIesmv6hVqly3w7gz1OJ3X8vQ+SbOQESFmSjsDfw5UXksfFAJ+VcNwDLcgXl5NSuOzu4cbXw7SQ==","signatures":[{"sig":"MEYCIQDCtTOcxo2SafJefhqWgjrRig614TmTnu8NfujGaFbWugIhAL9Mdl7oRsMWZa0G8iOQCYSlk34KAvNS2yKkklUOJF1p","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":31900},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"private":false,"scripts":{"test":"echo \"Error: no test specified\" && exit 1","build":"tsc -p tsconfig.json"},"_npmUser":{"name":"dylannormandin","email":"dtrnormandin@gmail.com"},"repository":{"url":"git+https://github.com/DylanNormandin/custom-packages.git","type":"git"},"_npmVersion":"10.8.2","directories":{},"_nodeVersion":"20.19.2","dependencies":{"pino":"^9.7.0","pino-stackdriver":"^3.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.9.2","@types/node":"^24.5.2","pino-pretty":"^13.0.0"},"_npmOperationalInternal":{"tmp":"tmp/logger_0.0.0_1758671452591_0.20086932198449325","host":"s3://npm-registry-packages-npm-production"}},"0.0.1":{"name":"@dylannormandin/logger","version":"0.0.1","keywords":[],"author":"","license":"ISC","_id":"@dylannormandin/logger@0.0.1","maintainers":[{"name":"dylannormandin","email":"dtrnormandin@gmail.com"}],"homepage":"https://github.com/DylanNormandin/custom-packages#readme","bugs":{"url":"https://github.com/DylanNormandin/custom-packages/issues"},"dist":{"shasum":"32da90581d757994c10dcf8348377c8000175b5e","tarball":"https://registry.npmjs.org/@dylannormandin/logger/-/logger-0.0.1.tgz","fileCount":5,"integrity":"sha512-AMr4FYxm00v0EUjgQRNQJhWNRk5w9MUsSj4nt3ENgHRDWElg0mnTBD2fWi6f2m+2I1yHxC7XOsWAE9PBHVCgKQ==","signatures":[{"sig":"MEUCIALVMwjirXoqlXYFzE7+BRth1qd67Fpc86J0LrDonU81AiEAhxq97wnGf00J9m88CEQEZL7P2m3BuVgDd+275U3/ZhE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":33971},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"3f369791f4712a22f40ac3b00a675b64d11746cf","private":false,"scripts":{"test":"echo \"Error: no test specified\" && exit 1","build":"tsc -p tsconfig.json"},"_npmUser":{"name":"dylannormandin","email":"dtrnormandin@gmail.com"},"repository":{"url":"git+https://github.com/DylanNormandin/custom-packages.git","type":"git"},"_npmVersion":"10.8.2","directories":{},"_nodeVersion":"20.19.2","dependencies":{"pino":"^9.7.0","pino-stackdriver":"^3.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.9.2","@types/node":"^24.5.2","pino-pretty":"^13.0.0"},"_npmOperationalInternal":{"tmp":"tmp/logger_0.0.1_1758765197889_0.6719623660667666","host":"s3://npm-registry-packages-npm-production"}},"0.0.2":{"name":"@dylannormandin/logger","version":"0.0.2","keywords":[],"author":"","license":"ISC","_id":"@dylannormandin/logger@0.0.2","maintainers":[{"name":"dylannormandin","email":"dtrnormandin@gmail.com"}],"homepage":"https://github.com/DylanNormandin/custom-packages#readme","bugs":{"url":"https://github.com/DylanNormandin/custom-packages/issues"},"dist":{"shasum":"14cf3ed43ca57df8ec1af320aeef3e369f382a00","tarball":"https://registry.npmjs.org/@dylannormandin/logger/-/logger-0.0.2.tgz","fileCount":6,"integrity":"sha512-Q0YWdhxRZN9rC05WHkScSPerQBD2LpJBZCzWZ3ymRuaAvkMDyZVKHVQfJhXjbdqd+ejGQU38EGvgQSusAZAhCg==","signatures":[{"sig":"MEUCIFnuLjfLTmOXbSemTVd1Q8+34zmcaJ+IjX7myX6m3FzHAiEAvBLNNd5ELRhZ9C3aohYsKwvanm+SVNlLiPQLNQtxReQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":41437},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"3f369791f4712a22f40ac3b00a675b64d11746cf","private":false,"scripts":{"test":"echo \"Error: no test specified\" && exit 1","build":"tsc -p tsconfig.json"},"_npmUser":{"name":"dylannormandin","email":"dtrnormandin@gmail.com"},"repository":{"url":"git+https://github.com/DylanNormandin/custom-packages.git","type":"git"},"_npmVersion":"10.8.2","description":"Shared logger for frontend (browser) and backend (Node). Built on Pino, with: - Module-scoped helpers: error, warn, info, debug - Dev browser → backend forwarding - Optional Google Cloud Logging (backend) - Configurable base URL and production forwarding ","directories":{},"_nodeVersion":"20.19.2","dependencies":{"pino":"^9.7.0","pino-stackdriver":"^3.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.9.2","@types/node":"^24.5.2","pino-pretty":"^13.0.0"},"_npmOperationalInternal":{"tmp":"tmp/logger_0.0.2_1758927107426_0.5177852400249834","host":"s3://npm-registry-packages-npm-production"}},"0.1.0":{"name":"@dylannormandin/logger","type":"module","version":"0.1.0","private":false,"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/DylanNormandin/custom-packages.git"},"scripts":{"build":"tsc -p tsconfig.json","test":"echo \"Error: no test specified\" && exit 1"},"keywords":[],"author":"","license":"ISC","description":"Shared logger for frontend (browser) and backend (Node). Built on Pino, with: - Module-scoped helpers: error, warn, info, debug - Transport-agnostic sinks API to forward logs anywhere (console, HTTP, GCP, etc.)","dependencies":{"pino":"^9.7.0","pino-stackdriver":"^3.0.0"},"devDependencies":{"@types/node":"^24.5.2","pino-pretty":"^13.0.0","typescript":"^5.9.2"},"_id":"@dylannormandin/logger@0.1.0","gitHead":"851cc4ed7beb3dcd7de7b23128a951856a620b4a","bugs":{"url":"https://github.com/DylanNormandin/custom-packages/issues"},"homepage":"https://github.com/DylanNormandin/custom-packages#readme","_nodeVersion":"20.19.2","_npmVersion":"10.8.2","dist":{"integrity":"sha512-dxX2Q8aRIsLfo1bSu4Nr5Q7+32HlZUm7fQW+JOFrK+NkYl0lmHkIb+M2L/jCRZmRocr4QOtBRdULVaYq01PrAQ==","shasum":"8583cfc0ceeaad817ca8df510484a63ef9e9bf7f","tarball":"https://registry.npmjs.org/@dylannormandin/logger/-/logger-0.1.0.tgz","fileCount":6,"unpackedSize":31247,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIFGKnBW++XZYB2X9Q18H11UGWE2Eoh1lC1tcZIm6DPc5AiAX4KObnwY3imNt0Uuf/PyXgJbok/1ezjuGGiRaReLSIw=="}]},"_npmUser":{"name":"dylannormandin","email":"dtrnormandin@gmail.com"},"directories":{},"maintainers":[{"name":"dylannormandin","email":"dtrnormandin@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/logger_0.1.0_1759014318728_0.7463977745070407"},"_hasShrinkwrap":false}},"time":{"created":"2025-09-23T23:50:52.481Z","modified":"2025-09-27T23:05:19.109Z","0.0.0":"2025-09-23T23:50:52.768Z","0.0.1":"2025-09-25T01:53:18.109Z","0.0.2":"2025-09-26T22:51:47.595Z","0.1.0":"2025-09-27T23:05:18.914Z"},"bugs":{"url":"https://github.com/DylanNormandin/custom-packages/issues"},"license":"ISC","homepage":"https://github.com/DylanNormandin/custom-packages#readme","keywords":[],"repository":{"type":"git","url":"git+https://github.com/DylanNormandin/custom-packages.git"},"description":"Shared logger for frontend (browser) and backend (Node). Built on Pino, with: - Module-scoped helpers: error, warn, info, debug - Transport-agnostic sinks API to forward logs anywhere (console, HTTP, GCP, etc.)","maintainers":[{"name":"dylannormandin","email":"dtrnormandin@gmail.com"}],"readme":"# @dylannormandin/logger\n\nShared logger for frontend (browser) and backend (Node). Built on Pino, with:\n- Module-scoped helpers: error, warn, info, debug\n- Transport-agnostic sinks API to forward logs anywhere (console, HTTP, GCP, etc.)\n\n### Install\n```bash\nnpm i @dylannormandin/logger\n```\n\n### Backend quick start (ESM/TypeScript)\n```ts\nimport { info, warn, error, debug, setLogContextProvider } from '@dylannormandin/logger';\n\n// Optional: enrich with request context (e.g., via AsyncLocalStorage)\nsetLogContextProvider(() => ({ correlationId: 'abc-123', traceId: '...' }));\n\ninfo('users', 'User signed in', { userId: '123' });\nwarn('billing', 'Payment slow', { invoiceId: 'abc' });\nerror('orders', 'Checkout failed', { orderId: 'xyz', err: new Error('boom') });\ndebug('db', 'Query timing', { ms: 42 });\n```\n- Filter level:\n```bash\nLOG_LEVEL=warn node server.js\n```\nOr at runtime:\n```ts\nimport { logger } from '@dylannormandin/logger';\nlogger.level = 'warn';\n```\n- Only log a specific module (backend only):\n```bash\nDEBUG_MODULE=auth node server.js\n```\n\n### Frontend quick start (Vite/React)\n```ts\nimport { info, warn, error, debug } from '@dylannormandin/logger';\n\ninfo('ui/home', 'Home mounted');\nerror('ui/cart', 'Remove item failed', { productId: 'p1' });\n```\n- Use sinks (below) to forward from the browser to your backend if desired.\n\n## Sinks (forward logs anywhere)\nRegister sinks once at app startup. Sinks receive structured events and can forward to stdout, HTTP, GCP, etc.\n\n```ts\nimport { setSinks, type LogEvent } from '@dylannormandin/logger';\n\n// Console sink (browser + node)\nconst consoleSink = (e: LogEvent) => {\n  const m = e.level === 'error' ? 'error' : e.level === 'warn' ? 'warn' : e.level === 'debug' ? 'debug' : 'log';\n  console[m](`[${e.module}] ${e.message}`, e.data);\n};\n\n// HTTP sink (browser or node) → your backend\nconst httpSink = async (e: LogEvent) => {\n  await fetch(`${BASE_URL}/api/logs`, {\n    method: 'POST',\n    headers: { 'Content-Type': 'application/json' },\n    body: JSON.stringify(e),\n  });\n};\n\n// GCP sink (backend)\nimport { Logging } from '@google-cloud/logging';\nconst gcpSink = (() => {\n  const logging = new Logging({\n    projectId: process.env.GOOGLE_PROJECT_ID,\n    credentials: process.env.GOOGLE_CLIENT_EMAIL && process.env.GOOGLE_PRIVATE_KEY\n      ? { client_email: process.env.GOOGLE_CLIENT_EMAIL, private_key: String(process.env.GOOGLE_PRIVATE_KEY).replace(/\\\\n/g, '\\n') }\n      : undefined,\n  });\n  const log = logging.log(process.env.GCP_LOG_NAME || 'app');\n  return async (e: LogEvent) => {\n    const metadata = { resource: { type: 'global' }, severity: e.level.toUpperCase() };\n    const entry = log.entry(metadata as any, { ...e.data, message: e.message, module: e.module, ts: e.timestamp });\n    await log.write(entry).catch(() => {});\n  };\n})();\n\n// Frontend: forward to backend\n// setSinks([consoleSink, httpSink]);\n\n// Backend: forward to Cloud Logging\n// setSinks([consoleSink, gcpSink]);\n```\n\n### Frontend → Backend HTTP sink + backend route\n- Backend route (Express, ESM/TypeScript):\n```ts\n// server/routes/log-intake.ts\nimport { Router } from 'express';\nimport { error, warn, info, debug } from '@dylannormandin/logger';\n\nexport const logRouter = Router();\n\nlogRouter.post('/api/logs', (req, res) => {\n  const { level = 'info', module = 'frontend', message = '', ...rest } = req.body || {};\n  const byLevel: Record<string, (m: string, msg: string, d?: Record<string, unknown>) => void> = { error, warn, info, debug };\n  (byLevel[level] || info)(module, message, rest);\n  res.sendStatus(204);\n});\n```\n\nWire in your app:\n```ts\n// server/index.ts\nimport express from 'express';\nimport cors from 'cors';\nimport { logRouter } from './routes/log-intake.js';\n\nconst app = express();\napp.use(cors({ origin: ['http://localhost:5173'] }));\napp.use(express.json({ limit: '200kb' }));\napp.use(logRouter);\napp.listen(3000);\n```\n\nEnv (local/non‑GCP):\n```env\nGOOGLE_PROJECT_ID=your-project-id\nGOOGLE_CLIENT_EMAIL=sa@your-project-id.iam.gserviceaccount.com\nGOOGLE_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\\n...\\n-----END PRIVATE KEY-----\\nGCP_LOG_NAME=my-app\n```\nOn GCP (Cloud Run/GKE/Compute), default credentials usually work; set `GCP_LOG_NAME` optionally.\n\n## Level filtering\n- Frontend (console + any sinks you add):\n```ts\nimport { logger } from '@dylannormandin/logger';\nlogger.level = 'warn';\n```\n- Backend: `LOG_LEVEL=warn` or `logger.level = 'warn'`.\n- Backend module filter: `DEBUG_MODULE=users`.\n\n## API\n- `error(module, message, data?)`\n- `warn(module, message, data?)`\n- `info(module, message, data?)`\n- `debug(module, message, data?)`\n- `logger` – underlying pino instance (change `logger.level` at runtime)\n- `setLogContextProvider(getContextFn)` – enrich logs with correlation/trace context\n- `setSinks(sinks)` – register transports (console, HTTP, GCP, etc.)\n- `createModuleLogger(module)` – child logger bound to a module\n- `shouldLogModule(module)` – backend helper honoring `DEBUG_MODULE`\n\n## Notes\n- ESM only; use ESM/TypeScript in consumers.\n","readmeFilename":"README.md"}