{"_id":"otel-instrumentation-postgres","name":"otel-instrumentation-postgres","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"otel-instrumentation-postgres","version":"1.0.0","description":"OpenTelemetry instrumentation for postgres.js","keywords":["postgresql","postgres.js","postgres","opentelemetry","instrumentation","tracing"],"author":{"name":"Wataru Oguchi","email":"wataru.oguchi.0to1@gmail.com"},"repository":{"type":"git","url":"git+https://github.com/wataruoguchi/otel-instrumentation-postgres.git"},"license":"Apache-2.0","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","exports":{"import":"./dist/index.js","require":"./dist/index.cjs"},"scripts":{"test":"vitest run","build":"tsup","prepare":"husky","release":"semantic-release","release:dry-run":"semantic-release --dry-run"},"devDependencies":{"@biomejs/biome":"^2.1.1","@semantic-release/commit-analyzer":"^13.0.1","@semantic-release/github":"^11.0.3","@semantic-release/npm":"^12.0.2","@semantic-release/release-notes-generator":"^14.0.3","@types/node":"^24.0.11","@vitest/coverage-v8":"^3.2.4","husky":"^9.1.7","lint-staged":"^16.1.2","postgres":"^3.4.7","semantic-release":"^24.2.7","tsup":"^8.5.0","typescript":"^5.8.3","vitest":"^3.2.4"},"lint-staged":{"*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}":["biome check --files-ignore-unknown=true","biome check --write --no-errors-on-unmatched","biome check --write --organize-imports-enabled=false --no-errors-on-unmatched","biome check --write --unsafe --no-errors-on-unmatched","biome format --write --no-errors-on-unmatched","biome lint --write --no-errors-on-unmatched"],"*":["biome check --no-errors-on-unmatched --files-ignore-unknown=true"]},"dependencies":{"@opentelemetry/instrumentation":"^0.202.0","@opentelemetry/semantic-conventions":"^1.34.0"},"peerDependencies":{"@opentelemetry/api":"^1.9.0"},"_id":"otel-instrumentation-postgres@1.0.0","gitHead":"ab802f1dd851bb775397ee5b3229ea748ee06faf","types":"./dist/index.d.ts","bugs":{"url":"https://github.com/wataruoguchi/otel-instrumentation-postgres/issues"},"homepage":"https://github.com/wataruoguchi/otel-instrumentation-postgres#readme","_nodeVersion":"20.19.3","_npmVersion":"10.9.3","dist":{"integrity":"sha512-osc0ApB0m5OWn2HO3uwsWzsUvsYMqTp5xC5LFA/SrfL4Y1WzTINt+Kwue+wMoHVormT3REH8Uzvo9h3mpEMsLQ==","shasum":"d630865b3c1a93466d03cf9afaa3bac8aa5de476","tarball":"https://registry.npmjs.org/otel-instrumentation-postgres/-/otel-instrumentation-postgres-1.0.0.tgz","fileCount":8,"unpackedSize":173203,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCpxcKDgs/1l55yxy+N9cIvkpvNU22dhOmfCwezJRMPYgIhAMWCPfhJxRfawUO0KvwXf3G/BdI9tt14ncHlnL9GQG6I"}]},"_npmUser":{"name":"wataru","email":"wataru.oguchi.0to1@gmail.com"},"directories":{},"maintainers":[{"name":"wataru","email":"wataru.oguchi.0to1@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/otel-instrumentation-postgres_1.0.0_1752388636822_0.364510433029396"},"_hasShrinkwrap":false}},"time":{"created":"2025-07-13T06:37:16.821Z","1.0.0":"2025-07-13T06:37:17.014Z","modified":"2025-07-13T06:37:17.236Z"},"maintainers":[{"name":"wataru","email":"wataru.oguchi.0to1@gmail.com"}],"description":"OpenTelemetry instrumentation for postgres.js","homepage":"https://github.com/wataruoguchi/otel-instrumentation-postgres#readme","keywords":["postgresql","postgres.js","postgres","opentelemetry","instrumentation","tracing"],"repository":{"type":"git","url":"git+https://github.com/wataruoguchi/otel-instrumentation-postgres.git"},"author":{"name":"Wataru Oguchi","email":"wataru.oguchi.0to1@gmail.com"},"bugs":{"url":"https://github.com/wataruoguchi/otel-instrumentation-postgres/issues"},"license":"Apache-2.0","readme":"# OpenTelemetry PostgreSQL Instrumentation\n\nA [OpenTelemetry](https://opentelemetry.io/) instrumentation library for the [`Postgres.js`](https://github.com/porsager/postgres) that provides observability for PostgreSQL database operations.\n\n## What This Library Measures\n\nThis instrumentation captures:\n\n### 📊 **Database Query Metrics**\n\n- **Query Duration**: Histogram of query execution times\n- **Query Count**: Total number of queries executed\n- **Error Count**: Number of failed queries\n- **Connection Count**: Number of database connections established\n- **Connection Duration**: How long connections remain active\n\n### 🔍 **Query Analysis**\n\n- **Operation Type**: SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, DROP\n- **Table Names**: Extracted from SQL queries\n- **Query Complexity**: Low/Medium/High based on query structure\n- **Query Characteristics**: Presence of WHERE, JOIN, ORDER BY, LIMIT clauses\n- **Parameter Count**: Number of query parameters used\n\n### 📈 **Performance Insights**\n\n- **Query Duration Distribution**: Histogram with configurable buckets\n- **Slow Query Detection**: Built-in support for identifying performance issues\n- **Connection Pool Monitoring**: Track connection lifecycle events\n\n## Installation\n\n```bash\nnpm install otel-instrumentation-postgres\n```\n\n## Quick Start\n\n### 1. Set up OpenTelemetry SDK\n\n```typescript\nimport { NodeSDK } from \"@opentelemetry/sdk-node\";\nimport { ConsoleSpanExporter } from \"@opentelemetry/sdk-trace-node\";\nimport { ConsoleMetricExporter, PeriodicExportingMetricReader } from \"@opentelemetry/sdk-metrics\";\nimport { PostgresInstrumentation } from \"otel-instrumentation-postgres\";\n\nconst sdk = new NodeSDK({\n  traceExporter: new ConsoleSpanExporter(),\n  metricReader: new PeriodicExportingMetricReader({\n    exporter: new ConsoleMetricExporter(),\n  }),\n  instrumentations: [\n    new PostgresInstrumentation({\n      serviceName: \"my-app\",\n      collectQueryParameters: true\n    }),\n  ],\n});\n\nsdk.start();\n```\n\n### 2. Wrap your postgres.js client\n\n```typescript\nimport postgres from \"postgres\";\nimport { createOTELEmitter } from \"otel-instrumentation-postgres\";\n\n// Create your postgres client\nconst sql = postgres(process.env.DATABASE_URL);\n\n// Wrap it with telemetry\nconst instrumentedSql = createOTELEmitter(sql);\n\n// Use the instrumented client - all queries are now tracked\nconst users = await instrumentedSql`SELECT * FROM users WHERE active = ${true}`;\n```\n\n## Configuration Options\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `serviceName` | `string` | - | Service name for telemetry attributes |\n| `enableHistogram` | `boolean` | `true` | Enable query duration histogram metrics |\n| `histogramBuckets` | `number[]` | `[0.001, 0.01, 0.1, 0.5, 1, 2, 5, 10, 30, 60, 120, 300, 600]` | Duration buckets in seconds |\n| `collectQueryParameters` | `boolean` | `false` | Include query parameters in spans |\n| `serverAddress` | `string` | `process.env.PGHOST` | Database server address |\n| `serverPort` | `number` | `process.env.PGPORT` | Database server port |\n| `databaseName` | `string` | `process.env.PGDATABASE` | Database name |\n| `parameterSanitizer` | `Function` | Built-in sanitizer | Custom parameter sanitization |\n| `beforeSpan` | `Function` | - | Hook called before span creation |\n| `afterSpan` | `Function` | - | Hook called after span completion |\n| `responseHook` | `Function` | - | Hook called with query result |\n\n## Generated Telemetry Data\n\n### Spans (Traces)\n\nEach database query generates a span with rich attributes:\n\n#### Standard OpenTelemetry Attributes\n\n- `db.system.name`: `\"postgresql\"`\n- `db.query.text`: Sanitized SQL query\n- `db.operation.name`: SQL operation (SELECT, INSERT, etc.)\n- `db.namespace`: Database name\n- `db.collection.name`: Extracted table name\n- `net.peer.name`: Database server address\n- `net.peer.port`: Database server port\n- `exception.type`: Error type for failed queries\n\n#### Custom Attributes\n\n- `db.parameter_count`: Number of query parameters\n- `db.duration_ms`: Query duration in milliseconds\n- `db.duration_seconds`: Query duration in seconds\n- `db.query.has_where`: Whether query has WHERE clause\n- `db.query.has_join`: Whether query has JOIN clause\n- `db.query.has_order_by`: Whether query has ORDER BY clause\n- `db.query.has_limit`: Whether query has LIMIT clause\n- `db.query.complexity`: Estimated query complexity (low/medium/high)\n- `db.query.type`: Query type (read/write/schema/unknown)\n- `db.result.row_count`: Number of rows returned (for arrays)\n\n#### Query Parameters (when enabled)\n\n- `db.query.parameter.0`, `db.query.parameter.1`, etc.: Individual query parameters (sanitized)\n\n### Metrics\n\n- `db.client.operations.duration`: Histogram of query durations\n- `db.client.requests`: Counter of total queries\n- `db.client.errors`: Counter of failed queries\n- `db.client.connections`: Counter of database connections\n- `db.client.connections.duration`: Histogram of connection durations\n\n## Advanced Usage\n\n### Custom Parameter Sanitization\n\n```typescript\nconst instrumentation = new PostgresInstrumentation({\n  parameterSanitizer: (param, index) => {\n    // Redact sensitive data\n    if (typeof param === \"string\") {\n      if (param.match(/^\\d{4}-\\d{4}-\\d{4}-\\d{4}$/)) {\n        return \"****-****-****-\" + param.slice(-4); // Credit card\n      }\n      if (param.includes(\"@\")) {\n        return \"[EMAIL]\"; // Email addresses\n      }\n    }\n    \n    // Truncate long values\n    const str = String(param);\n    return str.length > 50 ? str.substring(0, 50) + \"...\" : str;\n  },\n});\n```\n\n### Custom Hooks\n\n```typescript\nconst instrumentation = new PostgresInstrumentation({\n  serviceName: \"user-service\",\n  collectQueryParameters: true,\n  \n  // Add custom attributes before span creation\n  beforeSpan: (span, event) => {\n    span.setAttribute(\"user.id\", getCurrentUserId());\n    span.setAttribute(\"request.id\", getRequestId());\n  },\n  \n  // Monitor slow queries\n  afterSpan: (span, event) => {\n    if (event.durationMs > 1000) {\n      console.warn(`Slow query detected: ${event.sql} (${event.durationMs}ms)`);\n    }\n  },\n  \n  // Analyze query results\n  responseHook: (span, result) => {\n    if (Array.isArray(result)) {\n      span.setAttribute(\"db.result.count\", result.length);\n      \n      if (result.length === 0) {\n        span.setAttribute(\"db.result.empty\", true);\n      }\n    }\n  },\n});\n```\n\n### Query Analysis Examples\n\nThe library automatically analyzes your SQL queries:\n\n```typescript\n// This query will generate:\n// - operation: \"SELECT\"\n// - table: \"users\"\n// - has_where: true\n// - has_order_by: true\n// - has_limit: true\n// - complexity: \"medium\"\n// - type: \"read\"\nconst users = await instrumentedSql`\n  SELECT * FROM users\n  WHERE active = ${true}\n  ORDER BY created_at DESC\n  LIMIT 10\n`;\n\n// This query will generate:\n// - operation: \"INSERT\"\n// - table: \"users\"\n// - has_where: false\n// - complexity: \"low\"\n// - type: \"write\"\nawait instrumentedSql`\n  INSERT INTO users (name, email)\n  VALUES (${name}, ${email})\n`;\n```\n\n## Development\n\n### Prerequisites\n\n- Node.js 18+\n- npm\n\n### Setup\n\n```bash\n# Clone the repository\ngit clone https://github.com/wataruoguchi/otel-instrumentation-postgres.git\ncd otel-instrumentation-postgres\n\n# Install dependencies\ncd lib && npm install\ncd ../example && npm install\n\n# Run tests\ncd ../lib && npm test\n\n# Build the package\nnpm run build\n```\n\n## License\n\nThis project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details.\n\n## Acknowledgments\n\n- [Postgres.js](https://github.com/porsager/postgres) - The excellent PostgreSQL client\n- [OpenTelemetry](https://opentelemetry.io/) - The observability framework","readmeFilename":"README.md","_rev":"1-6c400bcbfaae6b45a1527934462eb410"}