{"_id":"@cannyminds/common","_rev":"2-fbb16120182aec5f2e80d6891357cb3d","name":"@cannyminds/common","dist-tags":{"latest":"2.0.0"},"versions":{"1.0.0":{"name":"@cannyminds/common","version":"1.0.0","keywords":["logging","pino","loki","express","middleware","utilities","cannyminds"],"author":{"name":"CannyMinds"},"license":"MIT","_id":"@cannyminds/common@1.0.0","maintainers":[{"name":"ganapathy-cm","email":"ganapathy@cannymindstech.com"}],"homepage":"https://github.com/cannyminds/common#readme","bugs":{"url":"https://github.com/cannyminds/common/issues"},"dist":{"shasum":"46f207a9485e4afc15f4728cf505c252fdff9c45","tarball":"https://registry.npmjs.org/@cannyminds/common/-/common-1.0.0.tgz","fileCount":14,"integrity":"sha512-huDPbX0dSiJYDhlTrPZhLQ/HD49BDBcxF3S1e2gbWhttdD7zByDYYO1gYUScxzCqSjgznaGlRoRklJx3uOBFRg==","signatures":[{"sig":"MEUCIDUJQzlyREXFFayKplPv9DIhCcgLsmPZnMfRCI0FwTq5AiEAgUsXLV6FDNyJ28uiGl6Qwee/Yk5U3PDXOkCUVYNZKMY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":124944},"main":"dist/index.js","_from":"file:cannyminds-common-1.0.0.tgz","types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"},"./log":{"types":"./dist/log/index.d.ts","import":"./dist/log/index.mjs","require":"./dist/log/index.js"}},"scripts":{"test":"vitest run","build":"tsup","clean":"rimraf dist","test:watch":"vitest","build:watch":"tsup --watch","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"ganapathy-cm","email":"ganapathy@cannymindstech.com"},"_resolved":"C:\\Users\\CODETE~1\\AppData\\Local\\Temp\\82383ae34958fef2787d1083729972cb\\cannyminds-common-1.0.0.tgz","_integrity":"sha512-huDPbX0dSiJYDhlTrPZhLQ/HD49BDBcxF3S1e2gbWhttdD7zByDYYO1gYUScxzCqSjgznaGlRoRklJx3uOBFRg==","repository":{"url":"git+https://github.com/cannyminds/common.git","type":"git"},"_npmVersion":"10.2.2","description":"Common utilities package for CannyMinds organization","directories":{},"_nodeVersion":"22.14.0","dependencies":{"pino":"^8.17.2","uuid":"^9.0.1","pino-loki":"^2.3.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.1","rimraf":"^5.0.5","vitest":"^1.1.0","express":"^4.18.2","supertest":"^6.3.4","typescript":"^5.3.3","@types/node":"^20.10.6","@types/uuid":"^9.0.7","pino-pretty":"^10.3.1","@types/express":"^4.17.21","@types/supertest":"^6.0.2","@vitest/coverage-v8":"^1.1.0"},"peerDependencies":{"express":"^4.18.0","pino-pretty":"^10.3.1"},"_npmOperationalInternal":{"tmp":"tmp/common_1.0.0_1767338026792_0.022062300132299706","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"name":"@cannyminds/common","version":"2.0.0","description":"Common utilities package with advanced logging, custom development formatter, Express middleware, and Loki integration","main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"},"./log":{"types":"./dist/log/index.d.ts","import":"./dist/log/index.mjs","require":"./dist/log/index.js"}},"keywords":["logging","pino","loki","express","middleware","utilities","cannyminds"],"author":{"name":"CannyMinds"},"license":"MIT","dependencies":{"pino":"^8.17.2","pino-loki":"^2.3.0","uuid":"^9.0.1"},"peerDependencies":{"express":"^4.18.0"},"devDependencies":{"@types/express":"^4.17.21","@types/node":"^20.10.6","@types/supertest":"^6.0.2","@types/uuid":"^9.0.7","@vitest/coverage-v8":"^1.1.0","express":"^4.18.2","rimraf":"^5.0.5","supertest":"^6.3.4","tsup":"^8.0.1","typescript":"^5.3.3","vitest":"^1.1.0"},"engines":{"node":">=18.0.0"},"repository":{"type":"git","url":"git+https://github.com/cannyminds/common.git"},"bugs":{"url":"https://github.com/cannyminds/common/issues"},"homepage":"https://github.com/cannyminds/common#readme","publishConfig":{"access":"public"},"scripts":{"build":"tsup","build:watch":"tsup --watch","clean":"rimraf dist","test":"vitest run","test:watch":"vitest","test:coverage":"vitest run --coverage"},"_id":"@cannyminds/common@2.0.0","_integrity":"sha512-Vxel2+wcVzmt6poN2K7t4K54mjep46VSajcbjTntXzw17QrIXFZBU/hoGG0yRBOfWLdPg41Q9C3YAgplZrPCiQ==","_resolved":"C:\\Users\\CODETE~1\\AppData\\Local\\Temp\\002c62036f001b9c9db9605d949abd46\\cannyminds-common-2.0.0.tgz","_from":"file:cannyminds-common-2.0.0.tgz","_nodeVersion":"22.14.0","_npmVersion":"10.2.2","dist":{"integrity":"sha512-Vxel2+wcVzmt6poN2K7t4K54mjep46VSajcbjTntXzw17QrIXFZBU/hoGG0yRBOfWLdPg41Q9C3YAgplZrPCiQ==","shasum":"da9f40ff4aaa011031b894235c50b60a35e4428d","tarball":"https://registry.npmjs.org/@cannyminds/common/-/common-2.0.0.tgz","fileCount":14,"unpackedSize":265726,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIHeWaLhH/6cn4+NNC9ApRkNcIhhXxM6Pj9taKq0uDZWJAiB2DbOvy25yhgRMGoGu//629H09aAppCgos5dDOKqXcfw=="}]},"_npmUser":{"name":"ganapathy-cm","email":"ganapathy@cannymindstech.com"},"directories":{},"maintainers":[{"name":"ganapathy-cm","email":"ganapathy@cannymindstech.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/common_2.0.0_1767519301292_0.38714275527170283"},"_hasShrinkwrap":false}},"time":{"created":"2026-01-02T07:13:46.714Z","modified":"2026-01-04T09:35:01.740Z","1.0.0":"2026-01-02T07:13:46.941Z","2.0.0":"2026-01-04T09:35:01.458Z"},"bugs":{"url":"https://github.com/cannyminds/common/issues"},"author":{"name":"CannyMinds"},"license":"MIT","homepage":"https://github.com/cannyminds/common#readme","keywords":["logging","pino","loki","express","middleware","utilities","cannyminds"],"repository":{"type":"git","url":"git+https://github.com/cannyminds/common.git"},"description":"Common utilities package with advanced logging, custom development formatter, Express middleware, and Loki integration","maintainers":[{"name":"ganapathy-cm","email":"ganapathy@cannymindstech.com"}],"readme":"# @cannyminds/common\r\n\r\nA comprehensive utilities package for CannyMinds organization, featuring advanced logging capabilities with Pino, Express middleware, and Loki integration.\r\n\r\n## Installation\r\n\r\n```bash\r\nnpm install @cannyminds/common\r\n```\r\n\r\n## Logging Module\r\n\r\nThe logging module provides a complete logging solution built on top of Pino with Express middleware and Loki HTTP client for querying logs.\r\n\r\n### Quick Start\r\n\r\n```typescript\r\nimport { createLogger, requestLoggerMiddleware, errorLoggerMiddleware } from '@cannyminds/common/log';\r\nimport express from 'express';\r\n\r\n// Create logger instance\r\nconst logger = createLogger({\r\n  serviceName: 'my-api',\r\n  level: 'info',\r\n  lokiEnabled: true,\r\n  lokiHost: 'http://localhost:3100'\r\n});\r\n\r\n// Setup Express app with logging middleware\r\nconst app = express();\r\napp.use(requestLoggerMiddleware(logger));\r\napp.use(errorLoggerMiddleware(logger));\r\n\r\n// Use in routes\r\napp.get('/users', (req, res) => {\r\n  req.logger.info('Fetching users');\r\n  req.logger.debug('database', { query: 'SELECT * FROM users' });\r\n  res.json({ users: [] });\r\n});\r\n\r\napp.listen(3000);\r\n```\r\n\r\n### Logger Configuration\r\n\r\nThe `createLogger` function accepts a configuration object with the following options:\r\n\r\n```typescript\r\ninterface LoggerConfig {\r\n  serviceName: string;           // Required: Name of your service\r\n  level?: LogLevel;             // Optional: Log level (default: 'info')\r\n  lokiHost?: string;            // Optional: Loki server URL\r\n  lokiEnabled?: boolean;        // Optional: Enable Loki transport (default: false)\r\n  lokiBasicAuth?: {             // Optional: Basic auth for Loki\r\n    username: string;\r\n    password: string;\r\n  };\r\n  additionalLabels?: Record<string, string>; // Optional: Extra labels\r\n  redactPaths?: string[];       // Optional: Paths to redact in logs\r\n}\r\n```\r\n\r\n#### Environment-Based Configuration\r\n\r\nThe logger automatically detects `NODE_ENV` for **output format only**:\r\n\r\n- **Development**: Uses custom colorized formatter with clean, human-readable output\r\n- **Production**: Uses JSON output with optional Loki transport\r\n\r\n**Note**: Log level is **not** automatically set based on environment. You have full control:\r\n\r\n```typescript\r\n// Same level works in both environments\r\nconst logger = createLogger({\r\n  serviceName: 'my-service',\r\n  level: 'debug'  // Will work in dev and prod\r\n});\r\n\r\n// You can set environment-specific levels manually if desired\r\nconst logger = createLogger({\r\n  serviceName: 'my-service',\r\n  level: process.env.NODE_ENV === 'production' ? 'warn' : 'debug'\r\n});\r\n```\r\n\r\n**Available Log Levels** (in order of severity):\r\n- `fatal` - Application crashes/exits\r\n- `error` - Error conditions that need attention\r\n- `warn` - Warning conditions (default for production in examples)\r\n- `info` - General information (default for all environments)\r\n- `debug` - Debug information for development\r\n- `trace` - Detailed trace information\r\n\r\n#### Example Configurations\r\n\r\n**Basic Logger:**\r\n```typescript\r\nconst logger = createLogger({\r\n  serviceName: 'user-service'\r\n});\r\n```\r\n\r\n**Production Logger with Loki:**\r\n```typescript\r\nconst logger = createLogger({\r\n  serviceName: 'user-service',\r\n  level: 'warn',  // Optional: reduce log noise in production\r\n  lokiEnabled: true,\r\n  lokiHost: 'https://loki.company.com',\r\n  lokiBasicAuth: {\r\n    username: process.env.LOKI_USERNAME!,\r\n    password: process.env.LOKI_PASSWORD!\r\n  },\r\n  additionalLabels: {\r\n    environment: process.env.NODE_ENV!,\r\n    version: process.env.APP_VERSION!\r\n  },\r\n  redactPaths: ['user.password', 'auth.token']\r\n});\r\n```\r\n\r\n### Express Middleware\r\n\r\n#### Request Logger Middleware\r\n\r\nAutomatically logs incoming requests and outgoing responses:\r\n\r\n```typescript\r\nimport { requestLoggerMiddleware } from '@cannyminds/common/log';\r\n\r\napp.use(requestLoggerMiddleware(logger));\r\n```\r\n\r\n**Features:**\r\n- Generates unique `requestId` for each request\r\n- Attaches `requestId` to request object and response headers\r\n- Creates child logger with `requestId` context\r\n- Logs request details: method, path, query, userAgent, IP\r\n- Logs response details: statusCode, duration, contentLength\r\n\r\n#### Error Logger Middleware\r\n\r\nCatches and logs errors with full context:\r\n\r\n```typescript\r\nimport { errorLoggerMiddleware } from '@cannyminds/common/log';\r\n\r\napp.use(errorLoggerMiddleware(logger));\r\n```\r\n\r\n**Features:**\r\n- Logs error details: name, message, stack trace\r\n- Includes request context: method, path, headers, body\r\n- Preserves `requestId` for tracing\r\n- Passes error to next error handler\r\n\r\n#### Using Request Logger in Routes\r\n\r\n```typescript\r\napp.get('/api/users/:id', (req, res) => {\r\n  const { id } = req.params;\r\n  \r\n  req.logger.info(`Fetching user ${id}`);\r\n  \r\n  try {\r\n    const user = await getUserById(id);\r\n    req.logger.debug('user-data', { userId: id, email: user.email });\r\n    res.json(user);\r\n  } catch (error) {\r\n    req.logger.error(error, { userId: id });\r\n    res.status(500).json({ error: 'Failed to fetch user' });\r\n  }\r\n});\r\n```\r\n\r\n### Loki Client\r\n\r\nQuery logs from Grafana Loki using the HTTP API:\r\n\r\n```typescript\r\nimport { LokiClient } from '@cannyminds/common/log';\r\n\r\nconst lokiClient = new LokiClient({\r\n  host: 'http://localhost:3100',\r\n  basicAuth: {\r\n    username: 'admin',\r\n    password: 'admin'\r\n  },\r\n  timeout: 30000,\r\n  defaultLimit: 1000\r\n});\r\n```\r\n\r\n#### Configuration Options\r\n\r\n```typescript\r\ninterface LokiClientConfig {\r\n  host: string;                 // Required: Loki server URL\r\n  basicAuth?: {                 // Optional: Basic authentication\r\n    username: string;\r\n    password: string;\r\n  };\r\n  timeout?: number;             // Optional: Request timeout (default: 30000ms)\r\n  defaultLimit?: number;        // Optional: Default query limit (default: 1000)\r\n}\r\n```\r\n\r\n#### Querying Logs\r\n\r\n**Point-in-time Query:**\r\n```typescript\r\nconst result = await lokiClient.query({\r\n  query: '{service=\"user-service\"} |= \"error\"',\r\n  limit: 100,\r\n  time: new Date()\r\n});\r\n```\r\n\r\n**Range Query:**\r\n```typescript\r\nconst logs = await lokiClient.queryRange({\r\n  query: '{service=\"user-service\", level=\"error\"}',\r\n  start: new Date(Date.now() - 3600000), // 1 hour ago\r\n  end: new Date(),\r\n  limit: 500,\r\n  direction: 'backward'\r\n});\r\n```\r\n\r\n**Get Available Labels:**\r\n```typescript\r\nconst labels = await lokiClient.getLabels();\r\nconst serviceNames = await lokiClient.getLabelValues('service');\r\n```\r\n\r\n**Tail Logs in Real-time:**\r\n```typescript\r\nfor await (const logEntry of lokiClient.tail('{service=\"user-service\"}')) {\r\n  console.log(`[${logEntry.timestamp}] ${logEntry.logLine}`);\r\n}\r\n```\r\n\r\n#### Common LogQL Queries\r\n\r\n```typescript\r\n// All errors in the last hour\r\nconst errors = await lokiClient.queryRange({\r\n  query: '{service=\"user-service\"} |= \"ERROR\"',\r\n  start: new Date(Date.now() - 3600000),\r\n  end: new Date()\r\n});\r\n\r\n// Specific user activity\r\nconst userLogs = await lokiClient.queryRange({\r\n  query: '{service=\"user-service\"} |~ \"userId.*123\"',\r\n  start: new Date(Date.now() - 86400000), // 24 hours\r\n  end: new Date()\r\n});\r\n\r\n// HTTP 500 errors\r\nconst serverErrors = await lokiClient.queryRange({\r\n  query: '{service=\"user-service\"} | json | statusCode=\"500\"',\r\n  start: new Date(Date.now() - 3600000),\r\n  end: new Date()\r\n});\r\n\r\n// Performance issues (slow requests)\r\nconst slowRequests = await lokiClient.queryRange({\r\n  query: '{service=\"user-service\"} | json | duration > 1000',\r\n  start: new Date(Date.now() - 3600000),\r\n  end: new Date()\r\n});\r\n```\r\n\r\n### Advanced Usage\r\n\r\n#### Child Loggers\r\n\r\nCreate contextual loggers for specific operations:\r\n\r\n```typescript\r\napp.post('/api/users', (req, res) => {\r\n  const operationId = uuidv4();\r\n  const operationLogger = req.logger.child({ \r\n    operationId, \r\n    operation: 'createUser' \r\n  });\r\n  \r\n  operationLogger.info('Starting user creation');\r\n  \r\n  // Use operationLogger throughout the request\r\n  operationLogger.debug('validation', { email: req.body.email });\r\n  operationLogger.info('User created successfully');\r\n});\r\n```\r\n\r\n#### Development Logger Output\r\n\r\nIn development, the logger provides clean, colorized output with different behaviors by log level:\r\n\r\n```typescript\r\n// Single-line output for info and warn (no object display)\r\nlogger.info('User logged in');              // Clean single line\r\nlogger.warn('High memory usage detected');  // Clean single line\r\n\r\n// Debug shows objects for inspection\r\nlogger.debug({ userId: '123', email: 'user@example.com' }, 'User data');\r\n// Output: User data\r\n//         {\r\n//           \"userId\": \"123\", \r\n//           \"email\": \"user@example.com\"\r\n//         }\r\n\r\n// Error shows stack traces automatically\r\ntry {\r\n  throw new Error('Something went wrong');\r\n} catch (err) {\r\n  logger.error({ err }, 'Database operation failed');\r\n  // Output: Database operation failed\r\n  //         Error: Something went wrong\r\n  //             at Object.<anonymous> (/path/to/file:10:15)\r\n  //             ...stack trace in red...\r\n}\r\n\r\n// Request logging with visual indicators\r\n// → POST /api/users    (incoming request)\r\n// ← 201 45ms          (outgoing response with color-coded duration)\r\n```\r\n\r\n**Output Features:**\r\n- **Timestamps** in gray\r\n- **Log levels** color-coded (INFO=green, WARN=yellow, ERROR=red, DEBUG=cyan)\r\n- **Service name** in bold brackets\r\n- **Request IDs** in cyan (last 8 characters)\r\n- **HTTP methods** color-coded (GET=green, POST=yellow, DELETE=red, etc.)\r\n- **Status codes** color-coded (2xx=green, 3xx=cyan, 4xx=yellow, 5xx=red)\r\n- **Durations** color-coded (fast=green, medium=yellow, slow=red)\r\n- **Stack traces** in red for errors\r\n- **Objects** pretty-printed for debug level only\r\n\r\n#### Custom Log Levels and Structured Logging\r\n\r\n```typescript\r\n// Different logging methods\r\nlogger.fatal('System is shutting down');\r\nlogger.error({ err: new Error('DB failed') }, 'Database connection failed');\r\nlogger.warn('High memory usage detected');\r\nlogger.info('User logged in');\r\nlogger.debug({ key: 'user:123', ttl: 300 }, 'Cache hit');\r\nlogger.trace('Function entry');\r\n```\r\n\r\n#### Environment Variables\r\n\r\nConfigure the logger using environment variables:\r\n\r\n```bash\r\n# .env file\r\nNODE_ENV=production\r\nLOG_LEVEL=info\r\nLOKI_HOST=https://loki.company.com\r\nLOKI_USERNAME=service_account\r\nLOKI_PASSWORD=secret_token\r\nSERVICE_NAME=user-api\r\n```\r\n\r\n```typescript\r\nconst logger = createLogger({\r\n  serviceName: process.env.SERVICE_NAME || 'unknown-service',\r\n  level: (process.env.LOG_LEVEL as LogLevel) || 'info',\r\n  lokiEnabled: !!process.env.LOKI_HOST,\r\n  lokiHost: process.env.LOKI_HOST,\r\n  lokiBasicAuth: process.env.LOKI_USERNAME ? {\r\n    username: process.env.LOKI_USERNAME,\r\n    password: process.env.LOKI_PASSWORD!\r\n  } : undefined\r\n});\r\n```\r\n\r\n## Development\r\n\r\n### Building the Package\r\n\r\n```bash\r\nnpm run build\r\n```\r\n\r\n### Testing\r\n\r\n```bash\r\nnpm test\r\n```\r\n\r\n### Publishing\r\n\r\n```bash\r\nnpm publish\r\n```\r\n\r\n## License\r\n\r\nMIT\r\n\r\n## Contributing\r\n\r\nPlease read our contributing guidelines before submitting pull requests.\r\n\r\n## Support\r\n\r\nFor issues and questions, please use the GitHub issue tracker.\r\n","readmeFilename":"README.md"}