diff --git a/docker-compose.yaml b/docker-compose.yaml index 1b62c93..99358a1 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -20,4 +20,31 @@ services: container_name: opentelemetry_jaeger_redis ports: - '16686:16686' - - '4318:4318' \ No newline at end of file + + otel-collector: + image: otel/opentelemetry-collector-contrib:latest + command: ["--config=/etc/otel-collector.yaml"] + volumes: + - ./otel-collector.yaml:/etc/otel-collector.yaml + ports: + - "9464:9464" + - "4318:4318" + - "4317:4317" + + prometheus: + image: prom/prometheus:latest + volumes: + - ./prometheus.yaml:/etc/prometheus/prometheus.yaml + command: ["--config.file=/etc/prometheus/prometheus.yaml"] + ports: + - "9090:9090" + + grafana: + image: grafana/grafana-enterprise + container_name: grafana + restart: unless-stopped + ports: + - '3001:3000' + environment: + GF_SECURITY_ADMIN_USER: "${GRAFANA_USER}" + GF_SECURITY_ADMIN_PASSWORD: "${GRAFANA_PASSWORD}" diff --git a/otel-collector.yaml b/otel-collector.yaml new file mode 100644 index 0000000..077ecec --- /dev/null +++ b/otel-collector.yaml @@ -0,0 +1,37 @@ +receivers: + otlp: + protocols: + http: + endpoint: 0.0.0.0:4318 + prometheus: + config: + scrape_configs: + - job_name: otel-collector + metrics_path: '/api/newsletter/metrics' + scrape_interval: 10s + static_configs: + - targets: + - host.docker.internal:3000 + +processors: + batch: + +exporters: + otlp: + endpoint: jaeger:4317 + tls: + insecure: true + + prometheus: + endpoint: 0.0.0.0:9464 + +service: + pipelines: + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlp] + metrics: + receivers: [otlp, prometheus] + processors: [batch] + exporters: [prometheus] diff --git a/package-lock.json b/package-lock.json index b500907..bb05338 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "@opentelemetry/sdk-metrics": "^1.27.0", "@opentelemetry/sdk-node": "^0.54.0", "@opentelemetry/sdk-trace-node": "^1.27.0", - "bullmq": "^5.21.2", + "bullmq": "^5.40.0", "bullmq-otel": "^1.0.1", "dotenv": "^16.4.5", "express": "^4.19.2", @@ -1590,12 +1590,12 @@ } }, "node_modules/bullmq": { - "version": "5.29.1", - "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.29.1.tgz", - "integrity": "sha512-TZWiwRlPnpaN+Qwh4D8IQf2cYLpkiDX1LbaaWEabc6y37ojIttWOSynxDewpVHyW233LssSIC4+aLMSvAjtpmg==", + "version": "5.40.0", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.40.0.tgz", + "integrity": "sha512-tmrk32EmcbtUOGPSdwlDUcc0w+nAMqCisk8vEFFmG8aOzIehz0BxTNSj6Grh0qoMugRF3VglWk8HGUBnWqU2Fw==", "license": "MIT", "dependencies": { - "cron-parser": "^4.6.0", + "cron-parser": "^4.9.0", "ioredis": "^5.4.1", "msgpackr": "^1.11.2", "node-abort-controller": "^3.1.1", diff --git a/package.json b/package.json index 453b925..723ac12 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "@opentelemetry/sdk-metrics": "^1.27.0", "@opentelemetry/sdk-node": "^0.54.0", "@opentelemetry/sdk-trace-node": "^1.27.0", - "bullmq": "^5.21.2", + "bullmq": "^5.40.0", "bullmq-otel": "^1.0.1", "dotenv": "^16.4.5", "express": "^4.19.2", diff --git a/prometheus.yaml b/prometheus.yaml new file mode 100644 index 0000000..c0c4323 --- /dev/null +++ b/prometheus.yaml @@ -0,0 +1,7 @@ +global: + scrape_interval: 10s + +scrape_configs: + - job_name: 'otel-collector' + static_configs: + - targets: ['otel-collector:9464'] \ No newline at end of file diff --git a/src/controllers/newsletter.controller.ts b/src/controllers/newsletter.controller.ts index 09cdaca..75e96a9 100644 --- a/src/controllers/newsletter.controller.ts +++ b/src/controllers/newsletter.controller.ts @@ -5,6 +5,7 @@ class NewsletterController { constructor() { this.subscribeToNewsletter = this.subscribeToNewsletter.bind(this); this.unsubcribeFromNewsletter = this.unsubcribeFromNewsletter.bind(this); + this.exportMetrics = this.exportMetrics.bind(this); } async subscribeToNewsletter(req: Request, res: Response, next: NextFunction) { @@ -32,6 +33,18 @@ class NewsletterController { return next(err); } } + + async exportMetrics(_: Request, res: Response, next: NextFunction) { + try { + const newsletterMetrics = await NewsletterService.exportMetrics(); + return res.writeHead(200, { + 'Content-Type': 'text/plain', + 'Content-Length': Buffer.byteLength(newsletterMetrics)} + ).end(newsletterMetrics); + } catch(err) { + return next(err); + } + } } export default new NewsletterController(); \ No newline at end of file diff --git a/src/routes/newsletter.route.ts b/src/routes/newsletter.route.ts index 730774f..57efbd3 100644 --- a/src/routes/newsletter.route.ts +++ b/src/routes/newsletter.route.ts @@ -9,4 +9,6 @@ router.post('/subscribe', newsletterController.subscribeToNewsletter); router.post('/unsubscribe', newsletterController.unsubcribeFromNewsletter); +router.get('/metrics', newsletterController.exportMetrics); + export default router; \ No newline at end of file diff --git a/src/services/newsletter.service.ts b/src/services/newsletter.service.ts index c4ff67b..4ceafa2 100644 --- a/src/services/newsletter.service.ts +++ b/src/services/newsletter.service.ts @@ -20,6 +20,10 @@ class NewsletterService { this.subscribedUserCRUD = SubscribedUserCrud; } + exportMetrics() { + return this.queue.exportPrometheusMetrics(); + } + async subscribeToNewsletter(email: string) { const subscribedUser = await this.subscribedUserCRUD.create(email); if (!subscribedUser) {