Skip to content

Commit

Permalink
chore: implement ELK stack
Browse files Browse the repository at this point in the history
  • Loading branch information
aliakkas006 committed May 9, 2024
1 parent 38be08e commit d37aa04
Show file tree
Hide file tree
Showing 75 changed files with 2,173 additions and 178 deletions.
2 changes: 1 addition & 1 deletion api-gateway/src/middlewares.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const auth = async (req: Request, res: Response, next: NextFunction) => {
try {
const token = req.headers['authorization']?.split(' ')[1];
const { data } = await axios.post(
'http://localhost:4003/auth/verify-token',
'http://auth:4003/auth/verify-token',
{
accessToken: token,
headers: {
Expand Down
107 changes: 67 additions & 40 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,27 +1,6 @@
version: '3.8'

services:
# Custom API Gateway --------------------------------
# api-gateway:
# build:
# context: ./api-gateway
# dockerfile: Dockerfile
# ports:
# - '8081:8081'
# environment:
# - PORT=8081
# - API_GATEWAY_URL='http://api-gateway:8081'
# volumes:
# - ./api-gateway:/app
# networks:
# - healthcare-appointment-system-network
# depends_on:
# - auth
# - user
# - email
# - redis-stack
# - rabbitmq

# Auth Service --------------------------------
auth:
build:
Expand Down Expand Up @@ -72,13 +51,13 @@ services:
- RABBITMQ_URL=amqp://guest:guest@rabbitmq:5672
- REDIS_HOST=redis-stack
- REDIS_PORT=6379
- ELASTICSEARCH_URL=http://elasticsearch:9200
volumes:
- ./services/appointment:/app
networks:
- healthcare-appointment-system-network
depends_on:
- postgres
- email
- rabbitmq
- redis-stack

Expand All @@ -103,23 +82,23 @@ services:
- rabbitmq
- redis-stack

# Notification Service -------------------------------
notification:
build:
context: ./services/notification
dockerfile: Dockerfile
ports:
- '4005:8080'
environment:
- DATABASE_URL=postgresql://admin:password@postgres:5432/notification_db?schema=public
- RABBITMQ_URL=amqp://guest:guest@rabbitmq:5672
volumes:
- ./services/notification:/app
networks:
- healthcare-appointment-system-network
depends_on:
- postgres
- rabbitmq
# # Notification Service -------------------------------
# notification:
# build:
# context: ./services/notification
# dockerfile: Dockerfile
# ports:
# - '4005:8080'
# environment:
# - DATABASE_URL=postgresql://admin:password@postgres:5432/notification_db?schema=public
# - RABBITMQ_URL=amqp://guest:guest@rabbitmq:5672
# volumes:
# - ./services/notification:/app
# networks:
# - healthcare-appointment-system-network
# depends_on:
# - postgres
# - rabbitmq

# Email Service -------------------------------
email:
Expand Down Expand Up @@ -148,7 +127,7 @@ services:
POSTGRES_PASSWORD: password
POSTGRES_DB: postgres
ports:
- '5433:5432'
- '5433:5432'
networks:
- healthcare-appointment-system-network
volumes:
Expand Down Expand Up @@ -210,6 +189,53 @@ services:
volumes:
- mailhog-data:/var/lib/mailhog

# Elasticsearch -------------------------------
elasticsearch:
image: elasticsearch:7.9.2
environment:
- discovery.type=single-node
- 'ES_JAVA_OPTS=-Xms1024m -Xmx1024m' # 1GB of memory allocated to JVM heap size
ports:
- '9200:9200' # HTTP port
- '9300:9300' # TCP port
networks:
- healthcare-appointment-system-network
volumes:
- elasticsearch-data:/var/lib/elasticsearch

# Kibana -----------------------------------
kibana:
image: kibana:7.9.2
environment:
- ELASTICSEARCH_URL=http://elasticsearch:9200
ports:
- '5601:5601' # HTTP port
networks:
- healthcare-appointment-system-network
depends_on:
- elasticsearch

# Custom API Gateway --------------------------------
# api-gateway:
# build:
# context: ./api-gateway
# dockerfile: Dockerfile
# ports:
# - '8081:8081'
# environment:
# - PORT=8081
# - API_GATEWAY_URL='http://api-gateway:8081'
# volumes:
# - ./api-gateway:/app
# networks:
# - healthcare-appointment-system-network
# depends_on:
# - auth
# - user
# - email
# - redis-stack
# - rabbitmq

# ------------------------------------------

networks:
Expand All @@ -221,3 +247,4 @@ volumes:
redis-data:
rabbitmq-data:
mailhog-data:
elasticsearch-data:
6 changes: 6 additions & 0 deletions services/EHR/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,13 @@
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"express-winston": "^4.2.0",
"ioredis": "^5.4.1",
"morgan": "^1.10.0",
"uuid": "^9.0.1",
"winston": "^3.13.0",
"winston-daily-rotate-file": "^5.0.0",
"winston-elasticsearch": "0.10.0",
"zod": "^3.22.4"
},
"devDependencies": {
Expand All @@ -28,6 +33,7 @@
"@types/jest": "^29.5.12",
"@types/morgan": "^1.9.9",
"@types/node": "^20.12.6",
"@types/uuid": "^9.0.8",
"jest": "^29.7.0",
"prisma": "^5.12.1",
"ts-jest": "^29.1.2",
Expand Down
27 changes: 22 additions & 5 deletions services/EHR/src/app.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
import express from 'express';
import express, { NextFunction, Request, Response } from 'express';
import cors from 'cors';
import morgan from 'morgan';
import routes from '@/routes';
import logger from '@/config/logger';
import setCorrelationId from '@/config/setCorrelationId';
import expressWinstonLogger from '@/config/expressWinstonLogger';

const app = express();

app.use([express.json(), cors(), morgan('dev')]);
app.use([
express.json(),
express.urlencoded({ extended: true }),
setCorrelationId,
expressWinstonLogger('http'),
cors(),
morgan('dev'),
]);

// Health check
app.get('/health', (_req, res) => {
Expand All @@ -21,9 +31,16 @@ app.use((_req, res) => {
});

// Global Error handler
app.use((err, _req, res, _next) => {
console.error(err.stack);
res.status(500).json({ error: 'Internal Server Error' });
app.use((err: any, req: Request, res: Response, _next: NextFunction) => {
const errObj = {
status: err?.status || 500,
message: err?.message || 'Something went wrong!',
errors: err?.errors || [],
correlationId: req.headers['x-correlation-id'],
};

logger.error(JSON.stringify(errObj));
res.status(errObj.status).json(errObj);
});

export default app;
File renamed without changes.
15 changes: 15 additions & 0 deletions services/EHR/src/config/expressWinstonLogger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import expressWinston from 'express-winston';
import logger from '@/config/logger';

const expressWinstonLogger = (level = 'http') => {
return expressWinston.logger({
level,
winstonInstance: logger,
meta: true,
msg: `HTTP {{res.statusCode}} {{req.method}} {{res.responseTime}}ms {{req.url}}`,
expressFormat: true,
colorize: true,
});
};

export default expressWinstonLogger;
9 changes: 9 additions & 0 deletions services/EHR/src/config/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { createLogger } from 'winston';
import 'winston-daily-rotate-file';
import { consoleTransport, elasticSearchTrnasport } from '@/utils/transports';

const logger = createLogger({
transports: [elasticSearchTrnasport, consoleTransport],
});

export default logger;
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Redis } from 'ioredis';
import { REDIS_HOST, REDIS_PORT } from '@/config';
import { REDIS_HOST, REDIS_PORT } from '@/config/config_url';

const redis = new Redis({
host: REDIS_HOST,
Expand Down
14 changes: 14 additions & 0 deletions services/EHR/src/config/setCorrelationId.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Request, Response, NextFunction } from 'express';
import { v4 as uuidv4 } from 'uuid';

const setCorrelationId = (req: Request, res: Response, next: NextFunction) => {
const key = 'x-correlation-id';
const correlationId = req.headers[key] || uuidv4();

req.headers[key] = correlationId;
res.set(key, correlationId);

next();
};

export default setCorrelationId;
7 changes: 3 additions & 4 deletions services/EHR/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import 'dotenv/config';
import http from 'http';
import app from './app';
import app from '@/app';
import logger from '@/config/logger';

const server = http.createServer(app);

const port = process.env.PORT || 4002;
const serviceName = process.env.SERVICE_NAME || 'EHR-Service';

server.listen(port, () => {
console.log(
`${serviceName} service is listening at http://localhost:${port}`
);
logger.info(`${serviceName} service is listening at http://ehr:${port}`);
});
24 changes: 13 additions & 11 deletions services/EHR/src/lib/EHRService.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import prisma from '@/prisma';
import sendToQueue from '@/queue';
import redis from '@/redis';
import prisma from '@/config/prisma';
import sendToQueue from '@/utils/queue';
import redis from '@/config/redis';
import logger from '@/config/logger';

class EHRService {
/**
Expand Down Expand Up @@ -60,13 +61,14 @@ class EHRService {
},
});

await sendToQueue('send-email', JSON.stringify(ehr));
logger.info('EHR Created Successfully: ', ehr);

await sendToQueue('send-email', JSON.stringify(ehr));
await redis.del('EHRs');

return ehr;
} catch (err) {
console.error('Error creating EHR:', err);
logger.error('Error creating EHR:', err);
throw err;
}
}
Expand All @@ -92,7 +94,7 @@ class EHRService {

return EHRs;
} catch (err) {
console.error('Error fetching EHRs:', err);
logger.error('Error fetching EHRs:', err);
throw err;
}
}
Expand Down Expand Up @@ -122,7 +124,7 @@ class EHRService {

return EHR;
} catch (err) {
console.error('Error fetching EHRs:', err);
logger.error('Error fetching EHRs:', err);
throw err;
}
}
Expand Down Expand Up @@ -157,7 +159,7 @@ class EHRService {

return EHR;
} catch (err) {
console.error('Error fetching EHR:', err);
logger.error('Error fetching EHR:', err);
throw err;
}
}
Expand All @@ -183,7 +185,7 @@ class EHRService {

return ehr;
} catch (err) {
console.error('Error updating EHR:', err);
logger.error('Error updating EHR:', err);
throw err;
}
}
Expand All @@ -207,7 +209,7 @@ class EHRService {

return ehr;
} catch (err) {
console.error('Error deleting EHR:', err);
logger.error('Error deleting EHR:', err);
throw err;
}
}
Expand All @@ -217,7 +219,7 @@ class EHRService {
* @returns Medications data
*/
public async getMedications() {
console.log('Fetching Medications from the database');
logger.info('Fetching Medications from the database');
return prisma.medication.findMany();
}

Expand Down
16 changes: 16 additions & 0 deletions services/EHR/src/utils/formatParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { TransformableInfo } from 'logform';

// Format params
const formatParams = ({
timestamp,
level,
message,
...args
}: TransformableInfo): string => {
const ts = timestamp.slice(0, 19).replace('T', ' ');
return `${ts} [${level}]: ${message} ${
Object.keys(args).length > 0 ? JSON.stringify(args) : ''
}`;
};

export default formatParams;
Loading

0 comments on commit d37aa04

Please sign in to comment.