diff --git a/kafka-analysis/kafka-analysis-1752114836141.json b/kafka-analysis/kafka-analysis-1752114836141.json
new file mode 100644
index 0000000..7794842
--- /dev/null
+++ b/kafka-analysis/kafka-analysis-1752114836141.json
@@ -0,0 +1,691 @@
+{
+ "clusterInfo": {
+ "clusterId": "MkU3OEVBNTcwNTJENDM2Qk",
+ "controller": 1,
+ "brokers": [
+ {
+ "nodeId": 1,
+ "host": "localhost",
+ "port": 29092
+ }
+ ],
+ "topics": 2
+ },
+ "topics": [
+ {
+ "name": "test_topic",
+ "partitions": 1,
+ "replicationFactor": 1,
+ "config": {
+ "undefined": {
+ "isDefault": true,
+ "isSensitive": false
+ }
+ },
+ "isInternal": false,
+ "errorCode": 0,
+ "errorMessage": "Topic has errors",
+ "vendor": "apache-kafka",
+ "partitionDetails": [
+ {
+ "id": 0,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ }
+ ]
+ },
+ {
+ "name": "__consumer_offsets",
+ "partitions": 50,
+ "replicationFactor": 1,
+ "config": {
+ "undefined": {
+ "isDefault": true,
+ "isSensitive": false
+ }
+ },
+ "isInternal": true,
+ "errorCode": 0,
+ "errorMessage": "Topic has errors",
+ "vendor": "apache-kafka",
+ "partitionDetails": [
+ {
+ "id": 7,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 1,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 42,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 13,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 25,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 36,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 31,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 37,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 43,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 18,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 24,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 8,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 14,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 30,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 29,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 32,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 26,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 38,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 0,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 17,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 41,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 20,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 47,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 49,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 15,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 23,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 44,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 12,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 9,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 3,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 6,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 35,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 10,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 45,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 16,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 4,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 33,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 28,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 39,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 40,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 5,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 34,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 21,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 46,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 11,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 27,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 2,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 22,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 19,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ },
+ {
+ "id": 48,
+ "leader": 1,
+ "replicas": [
+ 1
+ ],
+ "isr": [
+ 1
+ ]
+ }
+ ]
+ }
+ ],
+ "consumerGroups": [],
+ "summary": {
+ "totalTopics": 2,
+ "totalPartitions": 51,
+ "internalTopics": 1,
+ "userTopics": 1,
+ "topicsWithErrors": 0,
+ "consumerGroups": 0
+ },
+ "timestamp": "2025-07-10T02:33:56.075Z",
+ "healthChecks": {
+ "vendor": "apache-kafka",
+ "totalChecks": 14,
+ "passedChecks": 7,
+ "failedChecks": 1,
+ "warnings": 4,
+ "checks": [
+ {
+ "id": "replication-factor",
+ "name": "Replication Factor vs Broker Count",
+ "status": "pass",
+ "message": "All topics have appropriate replication factor (⤠1 brokers)",
+ "recommendation": null,
+ "description": "Checks if any topic has a replication factor greater than the number of brokers. Healthy: All topics have RF ⤠broker count. Failed: Any topic has RF > broker count."
+ },
+ {
+ "id": "partition-distribution",
+ "name": "Topic Partition Distribution",
+ "status": "pass",
+ "message": "Good partition distribution: avg=1.0, min=1, max=1",
+ "recommendation": null,
+ "description": "Checks if user topics have a balanced number of partitions. Healthy: Partition counts are similar. Warning: Large difference between min and max partitions."
+ },
+ {
+ "id": "consumer-groups",
+ "name": "Consumer Group Health",
+ "status": "info",
+ "message": "No consumer groups found",
+ "recommendation": null,
+ "description": "Checks if all consumer groups have active members. Healthy: All groups have members. Warning: Some groups have no active members."
+ },
+ {
+ "id": "internal-topics",
+ "name": "Internal Topics Health",
+ "status": "pass",
+ "message": "All 1 internal topics are healthy",
+ "recommendation": null,
+ "description": "Checks if all internal topics (names starting with __) have partitions > 0. Healthy: All internal topics have partitions. Failed: Any internal topic has 0 or missing partitions."
+ },
+ {
+ "id": "under-replicated-partitions",
+ "name": "Under-Replicated Partitions",
+ "status": "pass",
+ "message": "All topics have the expected number of in-sync replicas",
+ "recommendation": null,
+ "description": ""
+ },
+ {
+ "id": "min-insync-replicas",
+ "name": "Min In-Sync Replicas Configuration",
+ "status": "pass",
+ "message": "All topics have appropriate min.insync.replicas configuration",
+ "recommendation": null,
+ "description": ""
+ },
+ {
+ "id": "rack-awareness",
+ "name": "Rack Awareness",
+ "status": "warning",
+ "message": "Rack awareness is not configured - no brokers have rack information",
+ "recommendation": "Consider enabling rack awareness for better availability and fault tolerance",
+ "description": "Checks if rack awareness is configured in the cluster. Healthy: Rack awareness is configured. Warning: Rack awareness is not configured."
+ },
+ {
+ "id": "replica-distribution",
+ "name": "Replica Distribution",
+ "status": "pass",
+ "message": "Perfect replica balance: Each broker carries 51.0 replicas on average (range: 51-51)",
+ "recommendation": null,
+ "description": "Checks if data replicas are evenly distributed across all brokers. Healthy: Each broker carries a similar number of replicas. Warning/Failed: Some brokers carry significantly more replicas than others, which can cause performance issues."
+ },
+ {
+ "id": "metrics-enabled",
+ "name": "Metrics Configuration",
+ "status": "warning",
+ "message": "No JMX metrics configuration detected on any brokers",
+ "recommendation": "Enable JMX metrics on brokers for better monitoring, alerting, and performance analysis",
+ "description": "Checks if monitoring metrics are properly configured. For AWS MSK: Checks Open Monitoring with Prometheus JMX exporter. For others: Checks JMX metrics configuration. Healthy: Metrics are enabled and accessible. Warning: Metrics are not configured or partially configured."
+ },
+ {
+ "id": "logging-configuration",
+ "name": "Generic Kafka Logging Configuration",
+ "status": "info",
+ "message": "Generic Kafka logging configuration check",
+ "recommendation": "Verify log4j configuration and log directory permissions in server.properties",
+ "description": "Checks if logging configuration is properly configured. For AWS MSK: Checks LoggingInfo configuration and CloudTrail. For Confluent Cloud/Aiven: Built-in logging is available. For others: Checks log4j configuration. Healthy: Logging is enabled and configured. Warning: Logging is not configured or partially configured."
+ },
+ {
+ "id": "authentication-configuration",
+ "name": "Generic Kafka Authentication Configuration",
+ "status": "fail",
+ "message": "Unauthenticated access is enabled - this is a security risk",
+ "recommendation": "Enable SASL or SSL authentication in server.properties for better security",
+ "description": "Checks if unauthenticated access is enabled. For AWS MSK: Checks if SASL or SSL is configured. For Confluent Cloud/Aiven: Built-in authentication prevents unauthenticated access. For others: Checks if SASL or SSL is configured. Healthy: Authentication is enabled (no unauthenticated access). Failed: Unauthenticated access is enabled (security risk)."
+ },
+ {
+ "id": "quotas-configuration",
+ "name": "Generic Kafka Quotas Configuration",
+ "status": "warning",
+ "message": "No quota configuration detected in Kafka cluster",
+ "recommendation": "Configure quotas in server.properties or use kafka-configs.sh to set client quotas for better resource management",
+ "description": "Checks if Kafka quotas are configured and being used. For AWS MSK: Checks quota configuration via AWS console/CLI. For Confluent Cloud/Aiven: Built-in quota management is available. For others: Checks server.properties and kafka-configs.sh for quota settings. Healthy: Quotas are configured and managed. Info: Quotas configuration check available."
+ },
+ {
+ "id": "payload-compression",
+ "name": "Payload Compression",
+ "status": "warning",
+ "message": "No compression detected on any of the 1 user topics (0%)",
+ "recommendation": "Enable compression on topics to reduce storage usage and improve network performance",
+ "description": "Checks if payload compression is enabled on user topics. Analyzes compression.type, compression, and producer.compression.type configurations. Healthy: All user topics have compression enabled (100%). Warning: Some or no topics have compression enabled (<100%). Info: No user topics to analyze."
+ },
+ {
+ "id": "infinite-retention-policy",
+ "name": "Infinite Retention Policy",
+ "status": "pass",
+ "message": "No topics have infinite retention policy enabled",
+ "recommendation": null,
+ "description": "Checks if any topics have infinite retention policy enabled (retention.ms = infinite). Healthy: No topics have infinite retention. Warning: Some topics have infinite retention policy (bad practice). Info: Unable to verify retention policy."
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/kafka-analysis/kafka-health-checks-1752114836141.csv b/kafka-analysis/kafka-health-checks-1752114836141.csv
new file mode 100644
index 0000000..3a0f9f3
--- /dev/null
+++ b/kafka-analysis/kafka-health-checks-1752114836141.csv
@@ -0,0 +1,16 @@
+"Health Check Results"
+"Check Name","Status","Message","Description","Recommendation"
+"Replication Factor vs Broker Count","pass","All topics have appropriate replication factor (⤠1 brokers)","Checks if any topic has a replication factor greater than the number of brokers. Healthy: All topics have RF ⤠broker count. Failed: Any topic has RF > broker count.",""
+"Topic Partition Distribution","pass","Good partition distribution: avg=1.0, min=1, max=1","Checks if user topics have a balanced number of partitions. Healthy: Partition counts are similar. Warning: Large difference between min and max partitions.",""
+"Consumer Group Health","info","No consumer groups found","Checks if all consumer groups have active members. Healthy: All groups have members. Warning: Some groups have no active members.",""
+"Internal Topics Health","pass","All 1 internal topics are healthy","Checks if all internal topics (names starting with __) have partitions > 0. Healthy: All internal topics have partitions. Failed: Any internal topic has 0 or missing partitions.",""
+"Under-Replicated Partitions","pass","All topics have the expected number of in-sync replicas","",""
+"Min In-Sync Replicas Configuration","pass","All topics have appropriate min.insync.replicas configuration","",""
+"Rack Awareness","warning","Rack awareness is not configured - no brokers have rack information","Checks if rack awareness is configured in the cluster. Healthy: Rack awareness is configured. Warning: Rack awareness is not configured.","Consider enabling rack awareness for better availability and fault tolerance"
+"Replica Distribution","pass","Perfect replica balance: Each broker carries 51.0 replicas on average (range: 51-51)","Checks if data replicas are evenly distributed across all brokers. Healthy: Each broker carries a similar number of replicas. Warning/Failed: Some brokers carry significantly more replicas than others, which can cause performance issues.",""
+"Metrics Configuration","warning","No JMX metrics configuration detected on any brokers","Checks if monitoring metrics are properly configured. For AWS MSK: Checks Open Monitoring with Prometheus JMX exporter. For others: Checks JMX metrics configuration. Healthy: Metrics are enabled and accessible. Warning: Metrics are not configured or partially configured.","Enable JMX metrics on brokers for better monitoring, alerting, and performance analysis"
+"Generic Kafka Logging Configuration","info","Generic Kafka logging configuration check","Checks if logging configuration is properly configured. For AWS MSK: Checks LoggingInfo configuration and CloudTrail. For Confluent Cloud/Aiven: Built-in logging is available. For others: Checks log4j configuration. Healthy: Logging is enabled and configured. Warning: Logging is not configured or partially configured.","Verify log4j configuration and log directory permissions in server.properties"
+"Generic Kafka Authentication Configuration","fail","Unauthenticated access is enabled - this is a security risk","Checks if unauthenticated access is enabled. For AWS MSK: Checks if SASL or SSL is configured. For Confluent Cloud/Aiven: Built-in authentication prevents unauthenticated access. For others: Checks if SASL or SSL is configured. Healthy: Authentication is enabled (no unauthenticated access). Failed: Unauthenticated access is enabled (security risk).","Enable SASL or SSL authentication in server.properties for better security"
+"Generic Kafka Quotas Configuration","warning","No quota configuration detected in Kafka cluster","Checks if Kafka quotas are configured and being used. For AWS MSK: Checks quota configuration via AWS console/CLI. For Confluent Cloud/Aiven: Built-in quota management is available. For others: Checks server.properties and kafka-configs.sh for quota settings. Healthy: Quotas are configured and managed. Info: Quotas configuration check available.","Configure quotas in server.properties or use kafka-configs.sh to set client quotas for better resource management"
+"Payload Compression","warning","No compression detected on any of the 1 user topics (0%)","Checks if payload compression is enabled on user topics. Analyzes compression.type, compression, and producer.compression.type configurations. Healthy: All user topics have compression enabled (100%). Warning: Some or no topics have compression enabled (<100%). Info: No user topics to analyze.","Enable compression on topics to reduce storage usage and improve network performance"
+"Infinite Retention Policy","pass","No topics have infinite retention policy enabled","Checks if any topics have infinite retention policy enabled (retention.ms = infinite). Healthy: No topics have infinite retention. Warning: Some topics have infinite retention policy (bad practice). Info: Unable to verify retention policy.",""
\ No newline at end of file
diff --git a/kafka-analysis/kafka-report-1752114836141.html b/kafka-analysis/kafka-report-1752114836141.html
new file mode 100644
index 0000000..c57c81c
--- /dev/null
+++ b/kafka-analysis/kafka-report-1752114836141.html
@@ -0,0 +1,847 @@
+
+
+
+ Modern Kafka Health Report
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Health Check Summary
+
+
+
+
+
+
Health Check Results
+
+
+
+
+
+
+
+ Checks if unauthenticated access is enabled. For AWS MSK: Checks if SASL or SSL is configured. For Confluent Cloud/Aiven: Built-in authentication prevents unauthenticated access. For others: Checks if SASL or SSL is configured. Healthy: Authentication is enabled (no unauthenticated access). Failed: Unauthenticated access is enabled (security risk).
+
+
+ Unauthenticated access is enabled - this is a security risk
+
+
+
+
š” Recommendation: Enable SASL or SSL authentication in server.properties for better security
+
+
+
+
+
+
+
+ Checks if rack awareness is configured in the cluster. Healthy: Rack awareness is configured. Warning: Rack awareness is not configured.
+
+
+ Rack awareness is not configured - no brokers have rack information
+
+
+
+
š” Recommendation: Consider enabling rack awareness for better availability and fault tolerance
+
+
+
+
+
+
+
+ Checks if monitoring metrics are properly configured. For AWS MSK: Checks Open Monitoring with Prometheus JMX exporter. For others: Checks JMX metrics configuration. Healthy: Metrics are enabled and accessible. Warning: Metrics are not configured or partially configured.
+
+
+ No JMX metrics configuration detected on any brokers
+
+
+
+
š” Recommendation: Enable JMX metrics on brokers for better monitoring, alerting, and performance analysis
+
+
+
+
+
+
+
+ Checks if Kafka quotas are configured and being used. For AWS MSK: Checks quota configuration via AWS console/CLI. For Confluent Cloud/Aiven: Built-in quota management is available. For others: Checks server.properties and kafka-configs.sh for quota settings. Healthy: Quotas are configured and managed. Info: Quotas configuration check available.
+
+
+ No quota configuration detected in Kafka cluster
+
+
+
+
š” Recommendation: Configure quotas in server.properties or use kafka-configs.sh to set client quotas for better resource management
+
+
+
+
+
+
+
+ Checks if payload compression is enabled on user topics. Analyzes compression.type, compression, and producer.compression.type configurations. Healthy: All user topics have compression enabled (100%). Warning: Some or no topics have compression enabled (<100%). Info: No user topics to analyze.
+
+
+ No compression detected on any of the 1 user topics (0%)
+
+
+
+
š” Recommendation: Enable compression on topics to reduce storage usage and improve network performance
+
+
+
+
+
+
+
+ Checks if all consumer groups have active members. Healthy: All groups have members. Warning: Some groups have no active members.
+
+
+ No consumer groups found
+
+
+
+
+
+
+
+
+ Checks if logging configuration is properly configured. For AWS MSK: Checks LoggingInfo configuration and CloudTrail. For Confluent Cloud/Aiven: Built-in logging is available. For others: Checks log4j configuration. Healthy: Logging is enabled and configured. Warning: Logging is not configured or partially configured.
+
+
+ Generic Kafka logging configuration check
+
+
+
+
š” Recommendation: Verify log4j configuration and log directory permissions in server.properties
+
+
+
+
+
+
+
+ Checks if any topic has a replication factor greater than the number of brokers. Healthy: All topics have RF ⤠broker count. Failed: Any topic has RF > broker count.
+
+
+ All topics have appropriate replication factor (⤠1 brokers)
+
+
+
+
+
+
+
+
+ Checks if user topics have a balanced number of partitions. Healthy: Partition counts are similar. Warning: Large difference between min and max partitions.
+
+
+ Good partition distribution: avg=1.0, min=1, max=1
+
+
+
+
+
+
+
+
+ Checks if all internal topics (names starting with __) have partitions > 0. Healthy: All internal topics have partitions. Failed: Any internal topic has 0 or missing partitions.
+
+
+ All 1 internal topics are healthy
+
+
+
+
+
+
+
+
+ All topics have the expected number of in-sync replicas
+
+
+
+
+
+
+
+
+ All topics have appropriate min.insync.replicas configuration
+
+
+
+
+
+
+
+
+ Checks if data replicas are evenly distributed across all brokers. Healthy: Each broker carries a similar number of replicas. Warning/Failed: Some brokers carry significantly more replicas than others, which can cause performance issues.
+
+
+ Perfect replica balance: Each broker carries 51.0 replicas on average (range: 51-51)
+
+
+
+
+
+
+
+
+ Checks if any topics have infinite retention policy enabled (retention.ms = infinite). Healthy: No topics have infinite retention. Warning: Some topics have infinite retention policy (bad practice). Info: Unable to verify retention policy.
+
+
+ No topics have infinite retention policy enabled
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/kafka-analysis/kafka-summary-1752114836141.txt b/kafka-analysis/kafka-summary-1752114836141.txt
new file mode 100644
index 0000000..c85f013
--- /dev/null
+++ b/kafka-analysis/kafka-summary-1752114836141.txt
@@ -0,0 +1,76 @@
+Kafka Analysis Summary
+----------------------
+ZooKeepers: 1
+Brokers: 1
+Total Topics: 2
+User Topics: 1
+Internal Topics: 1
+Total Partitions: 51
+Topics with Issues: 0
+
+Health Check Results
+-------------------
+Total Checks: 14
+ā
Passed: 7
+ā Failed: 1
+ā ļø Warnings: 4
+
+ā
Replication Factor vs Broker Count
+ š Checks if any topic has a replication factor greater than the number of brokers. Healthy: All topics have RF ⤠broker count. Failed: Any topic has RF > broker count.
+ All topics have appropriate replication factor (⤠1 brokers)
+
+ā
Topic Partition Distribution
+ š Checks if user topics have a balanced number of partitions. Healthy: Partition counts are similar. Warning: Large difference between min and max partitions.
+ Good partition distribution: avg=1.0, min=1, max=1
+
+ā¹ļø Consumer Group Health
+ š Checks if all consumer groups have active members. Healthy: All groups have members. Warning: Some groups have no active members.
+ No consumer groups found
+
+ā
Internal Topics Health
+ š Checks if all internal topics (names starting with __) have partitions > 0. Healthy: All internal topics have partitions. Failed: Any internal topic has 0 or missing partitions.
+ All 1 internal topics are healthy
+
+ā
Under-Replicated Partitions
+ All topics have the expected number of in-sync replicas
+
+ā
Min In-Sync Replicas Configuration
+ All topics have appropriate min.insync.replicas configuration
+
+ā ļø Rack Awareness
+ š Checks if rack awareness is configured in the cluster. Healthy: Rack awareness is configured. Warning: Rack awareness is not configured.
+ Rack awareness is not configured - no brokers have rack information
+ š” Recommendation: Consider enabling rack awareness for better availability and fault tolerance
+
+ā
Replica Distribution
+ š Checks if data replicas are evenly distributed across all brokers. Healthy: Each broker carries a similar number of replicas. Warning/Failed: Some brokers carry significantly more replicas than others, which can cause performance issues.
+ Perfect replica balance: Each broker carries 51.0 replicas on average (range: 51-51)
+
+ā ļø Metrics Configuration
+ š Checks if monitoring metrics are properly configured. For AWS MSK: Checks Open Monitoring with Prometheus JMX exporter. For others: Checks JMX metrics configuration. Healthy: Metrics are enabled and accessible. Warning: Metrics are not configured or partially configured.
+ No JMX metrics configuration detected on any brokers
+ š” Recommendation: Enable JMX metrics on brokers for better monitoring, alerting, and performance analysis
+
+ā¹ļø Generic Kafka Logging Configuration
+ š Checks if logging configuration is properly configured. For AWS MSK: Checks LoggingInfo configuration and CloudTrail. For Confluent Cloud/Aiven: Built-in logging is available. For others: Checks log4j configuration. Healthy: Logging is enabled and configured. Warning: Logging is not configured or partially configured.
+ Generic Kafka logging configuration check
+ š” Recommendation: Verify log4j configuration and log directory permissions in server.properties
+
+ā Generic Kafka Authentication Configuration
+ š Checks if unauthenticated access is enabled. For AWS MSK: Checks if SASL or SSL is configured. For Confluent Cloud/Aiven: Built-in authentication prevents unauthenticated access. For others: Checks if SASL or SSL is configured. Healthy: Authentication is enabled (no unauthenticated access). Failed: Unauthenticated access is enabled (security risk).
+ Unauthenticated access is enabled - this is a security risk
+ š” Recommendation: Enable SASL or SSL authentication in server.properties for better security
+
+ā ļø Generic Kafka Quotas Configuration
+ š Checks if Kafka quotas are configured and being used. For AWS MSK: Checks quota configuration via AWS console/CLI. For Confluent Cloud/Aiven: Built-in quota management is available. For others: Checks server.properties and kafka-configs.sh for quota settings. Healthy: Quotas are configured and managed. Info: Quotas configuration check available.
+ No quota configuration detected in Kafka cluster
+ š” Recommendation: Configure quotas in server.properties or use kafka-configs.sh to set client quotas for better resource management
+
+ā ļø Payload Compression
+ š Checks if payload compression is enabled on user topics. Analyzes compression.type, compression, and producer.compression.type configurations. Healthy: All user topics have compression enabled (100%). Warning: Some or no topics have compression enabled (<100%). Info: No user topics to analyze.
+ No compression detected on any of the 1 user topics (0%)
+ š” Recommendation: Enable compression on topics to reduce storage usage and improve network performance
+
+ā
Infinite Retention Policy
+ š Checks if any topics have infinite retention policy enabled (retention.ms = infinite). Healthy: No topics have infinite retention. Warning: Some topics have infinite retention policy (bad practice). Info: Unable to verify retention policy.
+ No topics have infinite retention policy enabled
diff --git a/kafka-analysis/monitoring-2025-07-10T02-41-20-644Z.json b/kafka-analysis/monitoring-2025-07-10T02-41-20-644Z.json
new file mode 100644
index 0000000..0e3e1dc
--- /dev/null
+++ b/kafka-analysis/monitoring-2025-07-10T02-41-20-644Z.json
@@ -0,0 +1,163 @@
+{
+ "timestamp": "2025-07-10T02:41:20.644Z",
+ "type": "continuous_analysis",
+ "healthScore": 0,
+ "metrics": {
+ "topics": [
+ {
+ "name": "test_topic",
+ "replicationFactor": 0,
+ "isInternal": false
+ },
+ {
+ "name": "__consumer_offsets",
+ "replicationFactor": 0,
+ "isInternal": true
+ }
+ ],
+ "consumers": [],
+ "cluster": {
+ "brokers": 1,
+ "controller": 1,
+ "clusterId": "MkU3OEVBNTcwNTJENDM2Qk"
+ }
+ },
+ "healthResults": {
+ "vendor": "apache",
+ "totalChecks": 14,
+ "passedChecks": 7,
+ "failedChecks": 1,
+ "warnings": 4,
+ "checks": [
+ {
+ "id": "replication-factor",
+ "name": "Replication Factor vs Broker Count",
+ "status": "pass",
+ "message": "All topics have appropriate replication factor (⤠1 brokers)",
+ "recommendation": null,
+ "description": "Checks if any topic has a replication factor greater than the number of brokers. Healthy: All topics have RF ⤠broker count. Failed: Any topic has RF > broker count."
+ },
+ {
+ "id": "partition-distribution",
+ "name": "Topic Partition Distribution",
+ "status": "pass",
+ "message": "Good partition distribution: avg=1.0, min=1, max=1",
+ "recommendation": null,
+ "description": "Checks if user topics have a balanced number of partitions. Healthy: Partition counts are similar. Warning: Large difference between min and max partitions."
+ },
+ {
+ "id": "consumer-groups",
+ "name": "Consumer Group Health",
+ "status": "info",
+ "message": "No consumer groups found",
+ "recommendation": null,
+ "description": "Checks if all consumer groups have active members. Healthy: All groups have members. Warning: Some groups have no active members."
+ },
+ {
+ "id": "internal-topics",
+ "name": "Internal Topics Health",
+ "status": "pass",
+ "message": "All 1 internal topics are healthy",
+ "recommendation": null,
+ "description": "Checks if all internal topics (names starting with __) have partitions > 0. Healthy: All internal topics have partitions. Failed: Any internal topic has 0 or missing partitions."
+ },
+ {
+ "id": "under-replicated-partitions",
+ "name": "Under-Replicated Partitions",
+ "status": "pass",
+ "message": "All topics have the expected number of in-sync replicas",
+ "recommendation": null,
+ "description": ""
+ },
+ {
+ "id": "min-insync-replicas",
+ "name": "Min In-Sync Replicas Configuration",
+ "status": "pass",
+ "message": "All topics have appropriate min.insync.replicas configuration",
+ "recommendation": null,
+ "description": ""
+ },
+ {
+ "id": "rack-awareness",
+ "name": "Rack Awareness",
+ "status": "warning",
+ "message": "Rack awareness is not configured - no brokers have rack information",
+ "recommendation": "Consider enabling rack awareness for better availability and fault tolerance",
+ "description": "Checks if rack awareness is configured in the cluster. Healthy: Rack awareness is configured. Warning: Rack awareness is not configured."
+ },
+ {
+ "id": "replica-distribution",
+ "name": "Replica Distribution",
+ "status": "pass",
+ "message": "Perfect replica balance: Each broker carries 51.0 replicas on average (range: 51-51)",
+ "recommendation": null,
+ "description": "Checks if data replicas are evenly distributed across all brokers. Healthy: Each broker carries a similar number of replicas. Warning/Failed: Some brokers carry significantly more replicas than others, which can cause performance issues."
+ },
+ {
+ "id": "metrics-enabled",
+ "name": "Metrics Configuration",
+ "status": "warning",
+ "message": "No JMX metrics configuration detected on any brokers",
+ "recommendation": "Enable JMX metrics on brokers for better monitoring, alerting, and performance analysis",
+ "description": "Checks if monitoring metrics are properly configured. For AWS MSK: Checks Open Monitoring with Prometheus JMX exporter. For others: Checks JMX metrics configuration. Healthy: Metrics are enabled and accessible. Warning: Metrics are not configured or partially configured."
+ },
+ {
+ "id": "logging-configuration",
+ "name": "Generic Kafka Logging Configuration",
+ "status": "info",
+ "message": "Generic Kafka logging configuration check",
+ "recommendation": "Verify log4j configuration and log directory permissions in server.properties",
+ "description": "Checks if logging configuration is properly configured. For AWS MSK: Checks LoggingInfo configuration and CloudTrail. For Confluent Cloud/Aiven: Built-in logging is available. For others: Checks log4j configuration. Healthy: Logging is enabled and configured. Warning: Logging is not configured or partially configured."
+ },
+ {
+ "id": "authentication-configuration",
+ "name": "Generic Kafka Authentication Configuration",
+ "status": "fail",
+ "message": "Unauthenticated access is enabled - this is a security risk",
+ "recommendation": "Enable SASL or SSL authentication in server.properties for better security",
+ "description": "Checks if unauthenticated access is enabled. For AWS MSK: Checks if SASL or SSL is configured. For Confluent Cloud/Aiven: Built-in authentication prevents unauthenticated access. For others: Checks if SASL or SSL is configured. Healthy: Authentication is enabled (no unauthenticated access). Failed: Unauthenticated access is enabled (security risk)."
+ },
+ {
+ "id": "quotas-configuration",
+ "name": "Generic Kafka Quotas Configuration",
+ "status": "warning",
+ "message": "No quota configuration detected in Kafka cluster",
+ "recommendation": "Configure quotas in server.properties or use kafka-configs.sh to set client quotas for better resource management",
+ "description": "Checks if Kafka quotas are configured and being used. For AWS MSK: Checks quota configuration via AWS console/CLI. For Confluent Cloud/Aiven: Built-in quota management is available. For others: Checks server.properties and kafka-configs.sh for quota settings. Healthy: Quotas are configured and managed. Info: Quotas configuration check available."
+ },
+ {
+ "id": "payload-compression",
+ "name": "Payload Compression",
+ "status": "warning",
+ "message": "No compression detected on any of the 1 user topics (0%)",
+ "recommendation": "Enable compression on topics to reduce storage usage and improve network performance",
+ "description": "Checks if payload compression is enabled on user topics. Analyzes compression.type, compression, and producer.compression.type configurations. Healthy: All user topics have compression enabled (100%). Warning: Some or no topics have compression enabled (<100%). Info: No user topics to analyze."
+ },
+ {
+ "id": "infinite-retention-policy",
+ "name": "Infinite Retention Policy",
+ "status": "pass",
+ "message": "No topics have infinite retention policy enabled",
+ "recommendation": null,
+ "description": "Checks if any topics have infinite retention policy enabled (retention.ms = infinite). Healthy: No topics have infinite retention. Warning: Some topics have infinite retention policy (bad practice). Info: Unable to verify retention policy."
+ }
+ ]
+ },
+ "analysis": {
+ "status": "healthy",
+ "issues": [],
+ "improvements": [],
+ "insights": [
+ "All systems operating normally"
+ ],
+ "changesSinceBaseline": {}
+ },
+ "baseline": {
+ "timestamp": "2025-07-10T02:40:20.642Z",
+ "healthScore": 0,
+ "topicCount": 2,
+ "totalPartitions": null,
+ "consumerGroups": 0,
+ "totalLag": 0
+ }
+}
\ No newline at end of file
diff --git a/kafka-analysis/monitoring-2025-07-10T02-42-20-645Z.json b/kafka-analysis/monitoring-2025-07-10T02-42-20-645Z.json
new file mode 100644
index 0000000..9a583f9
--- /dev/null
+++ b/kafka-analysis/monitoring-2025-07-10T02-42-20-645Z.json
@@ -0,0 +1,163 @@
+{
+ "timestamp": "2025-07-10T02:42:20.645Z",
+ "type": "continuous_analysis",
+ "healthScore": 0,
+ "metrics": {
+ "topics": [
+ {
+ "name": "test_topic",
+ "replicationFactor": 0,
+ "isInternal": false
+ },
+ {
+ "name": "__consumer_offsets",
+ "replicationFactor": 0,
+ "isInternal": true
+ }
+ ],
+ "consumers": [],
+ "cluster": {
+ "brokers": 1,
+ "controller": 1,
+ "clusterId": "MkU3OEVBNTcwNTJENDM2Qk"
+ }
+ },
+ "healthResults": {
+ "vendor": "apache",
+ "totalChecks": 14,
+ "passedChecks": 7,
+ "failedChecks": 1,
+ "warnings": 4,
+ "checks": [
+ {
+ "id": "replication-factor",
+ "name": "Replication Factor vs Broker Count",
+ "status": "pass",
+ "message": "All topics have appropriate replication factor (⤠1 brokers)",
+ "recommendation": null,
+ "description": "Checks if any topic has a replication factor greater than the number of brokers. Healthy: All topics have RF ⤠broker count. Failed: Any topic has RF > broker count."
+ },
+ {
+ "id": "partition-distribution",
+ "name": "Topic Partition Distribution",
+ "status": "pass",
+ "message": "Good partition distribution: avg=1.0, min=1, max=1",
+ "recommendation": null,
+ "description": "Checks if user topics have a balanced number of partitions. Healthy: Partition counts are similar. Warning: Large difference between min and max partitions."
+ },
+ {
+ "id": "consumer-groups",
+ "name": "Consumer Group Health",
+ "status": "info",
+ "message": "No consumer groups found",
+ "recommendation": null,
+ "description": "Checks if all consumer groups have active members. Healthy: All groups have members. Warning: Some groups have no active members."
+ },
+ {
+ "id": "internal-topics",
+ "name": "Internal Topics Health",
+ "status": "pass",
+ "message": "All 1 internal topics are healthy",
+ "recommendation": null,
+ "description": "Checks if all internal topics (names starting with __) have partitions > 0. Healthy: All internal topics have partitions. Failed: Any internal topic has 0 or missing partitions."
+ },
+ {
+ "id": "under-replicated-partitions",
+ "name": "Under-Replicated Partitions",
+ "status": "pass",
+ "message": "All topics have the expected number of in-sync replicas",
+ "recommendation": null,
+ "description": ""
+ },
+ {
+ "id": "min-insync-replicas",
+ "name": "Min In-Sync Replicas Configuration",
+ "status": "pass",
+ "message": "All topics have appropriate min.insync.replicas configuration",
+ "recommendation": null,
+ "description": ""
+ },
+ {
+ "id": "rack-awareness",
+ "name": "Rack Awareness",
+ "status": "warning",
+ "message": "Rack awareness is not configured - no brokers have rack information",
+ "recommendation": "Consider enabling rack awareness for better availability and fault tolerance",
+ "description": "Checks if rack awareness is configured in the cluster. Healthy: Rack awareness is configured. Warning: Rack awareness is not configured."
+ },
+ {
+ "id": "replica-distribution",
+ "name": "Replica Distribution",
+ "status": "pass",
+ "message": "Perfect replica balance: Each broker carries 51.0 replicas on average (range: 51-51)",
+ "recommendation": null,
+ "description": "Checks if data replicas are evenly distributed across all brokers. Healthy: Each broker carries a similar number of replicas. Warning/Failed: Some brokers carry significantly more replicas than others, which can cause performance issues."
+ },
+ {
+ "id": "metrics-enabled",
+ "name": "Metrics Configuration",
+ "status": "warning",
+ "message": "No JMX metrics configuration detected on any brokers",
+ "recommendation": "Enable JMX metrics on brokers for better monitoring, alerting, and performance analysis",
+ "description": "Checks if monitoring metrics are properly configured. For AWS MSK: Checks Open Monitoring with Prometheus JMX exporter. For others: Checks JMX metrics configuration. Healthy: Metrics are enabled and accessible. Warning: Metrics are not configured or partially configured."
+ },
+ {
+ "id": "logging-configuration",
+ "name": "Generic Kafka Logging Configuration",
+ "status": "info",
+ "message": "Generic Kafka logging configuration check",
+ "recommendation": "Verify log4j configuration and log directory permissions in server.properties",
+ "description": "Checks if logging configuration is properly configured. For AWS MSK: Checks LoggingInfo configuration and CloudTrail. For Confluent Cloud/Aiven: Built-in logging is available. For others: Checks log4j configuration. Healthy: Logging is enabled and configured. Warning: Logging is not configured or partially configured."
+ },
+ {
+ "id": "authentication-configuration",
+ "name": "Generic Kafka Authentication Configuration",
+ "status": "fail",
+ "message": "Unauthenticated access is enabled - this is a security risk",
+ "recommendation": "Enable SASL or SSL authentication in server.properties for better security",
+ "description": "Checks if unauthenticated access is enabled. For AWS MSK: Checks if SASL or SSL is configured. For Confluent Cloud/Aiven: Built-in authentication prevents unauthenticated access. For others: Checks if SASL or SSL is configured. Healthy: Authentication is enabled (no unauthenticated access). Failed: Unauthenticated access is enabled (security risk)."
+ },
+ {
+ "id": "quotas-configuration",
+ "name": "Generic Kafka Quotas Configuration",
+ "status": "warning",
+ "message": "No quota configuration detected in Kafka cluster",
+ "recommendation": "Configure quotas in server.properties or use kafka-configs.sh to set client quotas for better resource management",
+ "description": "Checks if Kafka quotas are configured and being used. For AWS MSK: Checks quota configuration via AWS console/CLI. For Confluent Cloud/Aiven: Built-in quota management is available. For others: Checks server.properties and kafka-configs.sh for quota settings. Healthy: Quotas are configured and managed. Info: Quotas configuration check available."
+ },
+ {
+ "id": "payload-compression",
+ "name": "Payload Compression",
+ "status": "warning",
+ "message": "No compression detected on any of the 1 user topics (0%)",
+ "recommendation": "Enable compression on topics to reduce storage usage and improve network performance",
+ "description": "Checks if payload compression is enabled on user topics. Analyzes compression.type, compression, and producer.compression.type configurations. Healthy: All user topics have compression enabled (100%). Warning: Some or no topics have compression enabled (<100%). Info: No user topics to analyze."
+ },
+ {
+ "id": "infinite-retention-policy",
+ "name": "Infinite Retention Policy",
+ "status": "pass",
+ "message": "No topics have infinite retention policy enabled",
+ "recommendation": null,
+ "description": "Checks if any topics have infinite retention policy enabled (retention.ms = infinite). Healthy: No topics have infinite retention. Warning: Some topics have infinite retention policy (bad practice). Info: Unable to verify retention policy."
+ }
+ ]
+ },
+ "analysis": {
+ "status": "healthy",
+ "issues": [],
+ "improvements": [],
+ "insights": [
+ "All systems operating normally"
+ ],
+ "changesSinceBaseline": {}
+ },
+ "baseline": {
+ "timestamp": "2025-07-10T02:40:20.642Z",
+ "healthScore": 0,
+ "topicCount": 2,
+ "totalPartitions": null,
+ "consumerGroups": 0,
+ "totalLag": 0
+ }
+}
\ No newline at end of file
diff --git a/kafka-analysis/monitoring-2025-07-10T02-43-20-645Z.json b/kafka-analysis/monitoring-2025-07-10T02-43-20-645Z.json
new file mode 100644
index 0000000..59c4d87
--- /dev/null
+++ b/kafka-analysis/monitoring-2025-07-10T02-43-20-645Z.json
@@ -0,0 +1,163 @@
+{
+ "timestamp": "2025-07-10T02:43:20.645Z",
+ "type": "continuous_analysis",
+ "healthScore": 0,
+ "metrics": {
+ "topics": [
+ {
+ "name": "test_topic",
+ "replicationFactor": 0,
+ "isInternal": false
+ },
+ {
+ "name": "__consumer_offsets",
+ "replicationFactor": 0,
+ "isInternal": true
+ }
+ ],
+ "consumers": [],
+ "cluster": {
+ "brokers": 1,
+ "controller": 1,
+ "clusterId": "MkU3OEVBNTcwNTJENDM2Qk"
+ }
+ },
+ "healthResults": {
+ "vendor": "apache",
+ "totalChecks": 14,
+ "passedChecks": 7,
+ "failedChecks": 1,
+ "warnings": 4,
+ "checks": [
+ {
+ "id": "replication-factor",
+ "name": "Replication Factor vs Broker Count",
+ "status": "pass",
+ "message": "All topics have appropriate replication factor (⤠1 brokers)",
+ "recommendation": null,
+ "description": "Checks if any topic has a replication factor greater than the number of brokers. Healthy: All topics have RF ⤠broker count. Failed: Any topic has RF > broker count."
+ },
+ {
+ "id": "partition-distribution",
+ "name": "Topic Partition Distribution",
+ "status": "pass",
+ "message": "Good partition distribution: avg=1.0, min=1, max=1",
+ "recommendation": null,
+ "description": "Checks if user topics have a balanced number of partitions. Healthy: Partition counts are similar. Warning: Large difference between min and max partitions."
+ },
+ {
+ "id": "consumer-groups",
+ "name": "Consumer Group Health",
+ "status": "info",
+ "message": "No consumer groups found",
+ "recommendation": null,
+ "description": "Checks if all consumer groups have active members. Healthy: All groups have members. Warning: Some groups have no active members."
+ },
+ {
+ "id": "internal-topics",
+ "name": "Internal Topics Health",
+ "status": "pass",
+ "message": "All 1 internal topics are healthy",
+ "recommendation": null,
+ "description": "Checks if all internal topics (names starting with __) have partitions > 0. Healthy: All internal topics have partitions. Failed: Any internal topic has 0 or missing partitions."
+ },
+ {
+ "id": "under-replicated-partitions",
+ "name": "Under-Replicated Partitions",
+ "status": "pass",
+ "message": "All topics have the expected number of in-sync replicas",
+ "recommendation": null,
+ "description": ""
+ },
+ {
+ "id": "min-insync-replicas",
+ "name": "Min In-Sync Replicas Configuration",
+ "status": "pass",
+ "message": "All topics have appropriate min.insync.replicas configuration",
+ "recommendation": null,
+ "description": ""
+ },
+ {
+ "id": "rack-awareness",
+ "name": "Rack Awareness",
+ "status": "warning",
+ "message": "Rack awareness is not configured - no brokers have rack information",
+ "recommendation": "Consider enabling rack awareness for better availability and fault tolerance",
+ "description": "Checks if rack awareness is configured in the cluster. Healthy: Rack awareness is configured. Warning: Rack awareness is not configured."
+ },
+ {
+ "id": "replica-distribution",
+ "name": "Replica Distribution",
+ "status": "pass",
+ "message": "Perfect replica balance: Each broker carries 51.0 replicas on average (range: 51-51)",
+ "recommendation": null,
+ "description": "Checks if data replicas are evenly distributed across all brokers. Healthy: Each broker carries a similar number of replicas. Warning/Failed: Some brokers carry significantly more replicas than others, which can cause performance issues."
+ },
+ {
+ "id": "metrics-enabled",
+ "name": "Metrics Configuration",
+ "status": "warning",
+ "message": "No JMX metrics configuration detected on any brokers",
+ "recommendation": "Enable JMX metrics on brokers for better monitoring, alerting, and performance analysis",
+ "description": "Checks if monitoring metrics are properly configured. For AWS MSK: Checks Open Monitoring with Prometheus JMX exporter. For others: Checks JMX metrics configuration. Healthy: Metrics are enabled and accessible. Warning: Metrics are not configured or partially configured."
+ },
+ {
+ "id": "logging-configuration",
+ "name": "Generic Kafka Logging Configuration",
+ "status": "info",
+ "message": "Generic Kafka logging configuration check",
+ "recommendation": "Verify log4j configuration and log directory permissions in server.properties",
+ "description": "Checks if logging configuration is properly configured. For AWS MSK: Checks LoggingInfo configuration and CloudTrail. For Confluent Cloud/Aiven: Built-in logging is available. For others: Checks log4j configuration. Healthy: Logging is enabled and configured. Warning: Logging is not configured or partially configured."
+ },
+ {
+ "id": "authentication-configuration",
+ "name": "Generic Kafka Authentication Configuration",
+ "status": "fail",
+ "message": "Unauthenticated access is enabled - this is a security risk",
+ "recommendation": "Enable SASL or SSL authentication in server.properties for better security",
+ "description": "Checks if unauthenticated access is enabled. For AWS MSK: Checks if SASL or SSL is configured. For Confluent Cloud/Aiven: Built-in authentication prevents unauthenticated access. For others: Checks if SASL or SSL is configured. Healthy: Authentication is enabled (no unauthenticated access). Failed: Unauthenticated access is enabled (security risk)."
+ },
+ {
+ "id": "quotas-configuration",
+ "name": "Generic Kafka Quotas Configuration",
+ "status": "warning",
+ "message": "No quota configuration detected in Kafka cluster",
+ "recommendation": "Configure quotas in server.properties or use kafka-configs.sh to set client quotas for better resource management",
+ "description": "Checks if Kafka quotas are configured and being used. For AWS MSK: Checks quota configuration via AWS console/CLI. For Confluent Cloud/Aiven: Built-in quota management is available. For others: Checks server.properties and kafka-configs.sh for quota settings. Healthy: Quotas are configured and managed. Info: Quotas configuration check available."
+ },
+ {
+ "id": "payload-compression",
+ "name": "Payload Compression",
+ "status": "warning",
+ "message": "No compression detected on any of the 1 user topics (0%)",
+ "recommendation": "Enable compression on topics to reduce storage usage and improve network performance",
+ "description": "Checks if payload compression is enabled on user topics. Analyzes compression.type, compression, and producer.compression.type configurations. Healthy: All user topics have compression enabled (100%). Warning: Some or no topics have compression enabled (<100%). Info: No user topics to analyze."
+ },
+ {
+ "id": "infinite-retention-policy",
+ "name": "Infinite Retention Policy",
+ "status": "pass",
+ "message": "No topics have infinite retention policy enabled",
+ "recommendation": null,
+ "description": "Checks if any topics have infinite retention policy enabled (retention.ms = infinite). Healthy: No topics have infinite retention. Warning: Some topics have infinite retention policy (bad practice). Info: Unable to verify retention policy."
+ }
+ ]
+ },
+ "analysis": {
+ "status": "healthy",
+ "issues": [],
+ "improvements": [],
+ "insights": [
+ "All systems operating normally"
+ ],
+ "changesSinceBaseline": {}
+ },
+ "baseline": {
+ "timestamp": "2025-07-10T02:40:20.642Z",
+ "healthScore": 0,
+ "topicCount": 2,
+ "totalPartitions": null,
+ "consumerGroups": 0,
+ "totalLag": 0
+ }
+}
\ No newline at end of file
diff --git a/kraft-config.json b/kraft-config.json
new file mode 100644
index 0000000..27d9297
--- /dev/null
+++ b/kraft-config.json
@@ -0,0 +1,23 @@
+{
+ "kafka": {
+ "brokers": ["localhost:29092"],
+ "clientId": "superstream-analyzer",
+ "vendor": "apache-kafka",
+ "useSasl": false
+ },
+ "monitoring": {
+ "enabled": false,
+ "interval": 300,
+ "metricsCollection": {
+ "enabled": true,
+ "interval": 60
+ }
+ },
+ "file": {
+ "outputDir": "./kafka-analysis",
+ "formats": ["json", "html", "csv", "txt"],
+ "includeMetadata": true,
+ "includeTimestamp": true
+ },
+ "email": "test@example.com"
+}
diff --git a/src/cli.js b/src/cli.js
index 4780b15..8755d2d 100644
--- a/src/cli.js
+++ b/src/cli.js
@@ -46,6 +46,23 @@ class CLI {
this.config = config;
+ // Normalize brokers/bootstrap_servers to array format
+ if (this.config.kafka) {
+ // Handle both 'bootstrap_servers' and 'brokers' fields
+ const brokers = this.config.kafka.bootstrap_servers || this.config.kafka.brokers;
+
+ if (brokers) {
+ if (Array.isArray(brokers)) {
+ this.config.kafka.brokers = brokers;
+ } else if (typeof brokers === 'string') {
+ this.config.kafka.brokers = brokers.split(',').map(broker => broker.trim());
+ }
+
+ // Remove bootstrap_servers field if it exists, standardize on 'brokers'
+ delete this.config.kafka.bootstrap_servers;
+ }
+ }
+
// Add email if not present in config file
if (!this.config.email) {
this.config.email = '';
@@ -261,6 +278,10 @@ class CLI {
// Build kafka config
this.config.kafka = {
...kafkaAnswers,
+ // Convert brokers string to array
+ brokers: Array.isArray(kafkaAnswers.brokers)
+ ? kafkaAnswers.brokers
+ : kafkaAnswers.brokers.split(',').map(broker => broker.trim()),
vendor: vendorAnswer.vendor,
useSasl: !!saslConfig,
sasl: saslConfig
@@ -321,6 +342,41 @@ class CLI {
this.config.email = emailAnswer.email;
}
+ async promptForMonitoring() {
+ console.log(chalk.yellow('\nš Monitoring Options'));
+
+ const questions = [
+ {
+ type: 'confirm',
+ name: 'startMonitoring',
+ message: 'Would you like to start continuous monitoring of your Kafka cluster?',
+ default: false
+ },
+ {
+ type: 'list',
+ name: 'interval',
+ message: 'How often should we run health checks?',
+ choices: [
+ { name: 'Every 1 minute (Quick monitoring)', value: 60 },
+ { name: 'Every 5 minutes (Balanced)', value: 300 },
+ { name: 'Every 15 minutes (Light monitoring)', value: 900 },
+ { name: 'Every 30 minutes (Minimal)', value: 1800 }
+ ],
+ default: 300,
+ when: (answers) => answers.startMonitoring
+ },
+ {
+ type: 'confirm',
+ name: 'enableMetrics',
+ message: 'Enable real-time metrics collection? (Recommended)',
+ default: true,
+ when: (answers) => answers.startMonitoring
+ }
+ ];
+
+ return await inquirer.prompt(questions);
+ }
+
async run() {
try {
// Track app start with location
@@ -342,17 +398,40 @@ class CLI {
process.exit(1);
}
console.log(chalk.gray('Debug: Config loaded successfully, skipping prompts'));
+
+ // Check if monitoring is enabled in config
+ if (this.config.monitoring?.enabled) {
+ console.log(chalk.blue('š Monitoring enabled in configuration file'));
+ await this.startContinuousMonitoring();
+ return; // Exit early for monitoring mode
+ }
} else {
console.log(chalk.gray('Debug: No config file specified, using interactive mode'));
await this.promptForConfig();
}
- // Initialize services without validation
- console.log(chalk.yellow('\nā ļø Validation skipped - proceeding directly to analysis'));
- this.kafkaClient = new KafkaClient(this.config.kafka);
- this.fileService = new FileService(this.config.file);
-
- await this.analyzeKafka();
+ // Ask about monitoring (only in interactive mode)
+ const monitoringChoice = await this.promptForMonitoring();
+
+ if (monitoringChoice.startMonitoring) {
+ // Update config with monitoring preferences
+ this.config.monitoring = {
+ enabled: true,
+ interval: monitoringChoice.interval,
+ metricsCollection: {
+ enabled: monitoringChoice.enableMetrics,
+ interval: Math.min(monitoringChoice.interval / 5, 60) // Metrics 5x more frequent than analysis, max 60s
+ }
+ };
+
+ await this.startContinuousMonitoring();
+ } else {
+ // Run single analysis as before
+ console.log(chalk.yellow('\nā ļø Validation skipped - proceeding directly to analysis'));
+ this.kafkaClient = new KafkaClient(this.config.kafka);
+ this.fileService = new FileService(this.config.file);
+ await this.analyzeKafka();
+ }
} catch (error) {
// Track errors with location
await this.analytics.trackLocationBasedEvent('error', {
@@ -669,6 +748,85 @@ class CLI {
message: `Location: ${location.city}, ${location.region}, ${location.country}`
};
}
+
+ async startContinuousMonitoring() {
+ console.log(chalk.blue('\nš Starting Continuous Kafka Monitoring'));
+
+ const { ContinuousMonitor } = require('./monitoring/continuous-monitor');
+ const monitor = new ContinuousMonitor(this.config);
+
+ // Setup event handlers
+ monitor.on('started', () => {
+ console.log(chalk.green('ā
Continuous monitoring is now active'));
+ });
+
+ monitor.on('analysis:complete', (report) => {
+ // Track successful monitoring cycles
+ this.analytics.trackEvent('monitoring_cycle_complete', {
+ vendor: this.config.kafka.vendor,
+ health_score: report.healthScore,
+ issues_count: report.analysis.issues.length
+ });
+ });
+
+ monitor.on('analysis:error', (error) => {
+ console.error(chalk.red('šØ Monitoring error:'), error.message);
+ this.analytics.trackError('monitoring_error', this.config.kafka.vendor);
+ });
+
+ monitor.on('consecutive:failures', (count) => {
+ console.log(chalk.red(`šØ ALERT: ${count} consecutive monitoring failures detected!`));
+ console.log(chalk.yellow('š” This may indicate a serious cluster issue or connectivity problem'));
+
+ this.analytics.trackEvent('monitoring_consecutive_failures', {
+ vendor: this.config.kafka.vendor,
+ failure_count: count
+ });
+ });
+
+ // Setup graceful shutdown
+ this.setupGracefulShutdown(monitor);
+
+ try {
+ await monitor.start();
+
+ // Keep the process alive
+ this.keepProcessAlive();
+
+ } catch (error) {
+ console.error(chalk.red('ā Failed to start monitoring:'), error.message);
+ process.exit(1);
+ }
+ }
+
+ setupGracefulShutdown(monitor) {
+ const shutdown = async (signal) => {
+ console.log(chalk.yellow(`\nš“ Received ${signal}, shutting down gracefully...`));
+
+ try {
+ await monitor.stop();
+ console.log(chalk.green('š Monitoring stopped successfully'));
+ process.exit(0);
+ } catch (error) {
+ console.error(chalk.red('ā Error during shutdown:'), error.message);
+ process.exit(1);
+ }
+ };
+
+ process.on('SIGINT', () => shutdown('SIGINT'));
+ process.on('SIGTERM', () => shutdown('SIGTERM'));
+
+ process.on('uncaughtException', async (error) => {
+ console.error(chalk.red('ā Uncaught Exception:'), error);
+ await shutdown('uncaughtException');
+ });
+ }
+
+ keepProcessAlive() {
+ setInterval(() => {
+ // Keep the process running for monitoring
+ }, 1000);
+ }
}
// At the top of the file, after class CLI definition
@@ -715,4 +873,4 @@ async function runCLI(options) {
await cli.run();
}
-module.exports = { CLI, runCLI };
\ No newline at end of file
+module.exports = { CLI, runCLI };
\ No newline at end of file
diff --git a/src/monitoring/continuous-monitor.js b/src/monitoring/continuous-monitor.js
new file mode 100644
index 0000000..55e570e
--- /dev/null
+++ b/src/monitoring/continuous-monitor.js
@@ -0,0 +1,450 @@
+const EventEmitter = require('events');
+const { KafkaClient } = require('../kafka-client');
+const { HealthChecker } = require('../health-checker');
+const chalk = require('chalk');
+const fs = require('fs').promises;
+const path = require('path');
+
+class ContinuousMonitor extends EventEmitter {
+ constructor(config) {
+ super();
+ this.config = config;
+ this.kafkaClient = new KafkaClient(config.kafka);
+ this.healthChecker = new HealthChecker(config.kafka.vendor, config.kafka);
+ this.isRunning = false;
+ this.intervalId = null;
+ this.metricsIntervalId = null;
+ this.consecutiveFailures = 0;
+ this.lastHealthScore = null;
+ this.baseline = null;
+
+ this.thresholds = {
+ healthScoreChange: 10,
+ consecutiveFailureLimit: 3,
+ significantLagIncrease: 50,
+ newIssueDetection: true
+ };
+
+ this.monitoringConfig = {
+ interval: config.monitoring?.interval || 300,
+ metricsInterval: config.monitoring?.metricsCollection?.interval || 60,
+ enabled: config.monitoring?.enabled !== false,
+ metricsEnabled: config.monitoring?.metricsCollection?.enabled !== false
+ };
+ }
+
+ async start() {
+ if (this.isRunning) {
+ console.log(chalk.yellow('ā ļø Monitoring is already running'));
+ return;
+ }
+
+ console.log(chalk.blue('š Starting Continuous Kafka Monitoring...'));
+ console.log(chalk.gray(`š Analysis interval: ${this.monitoringConfig.interval} seconds`));
+ console.log(chalk.gray(`š Metrics collection interval: ${this.monitoringConfig.metricsInterval} seconds`));
+
+ try {
+ await this.kafkaClient.connect();
+ console.log(chalk.green('ā
Initial connection successful'));
+
+ await this.establishBaseline();
+
+ this.startAnalysisInterval();
+
+ if (this.monitoringConfig.metricsEnabled) {
+ this.startMetricsInterval();
+ }
+
+ this.isRunning = true;
+ this.emit('started');
+
+ console.log(chalk.green('ā
Continuous monitoring started successfully'));
+ console.log(chalk.gray('š” Press Ctrl+C to stop monitoring'));
+
+ } catch (error) {
+ console.error(chalk.red('ā Failed to start monitoring:'), error.message);
+ this.emit('error', error);
+ throw error;
+ }
+ }
+
+ async establishBaseline() {
+ console.log(chalk.blue('š Establishing baseline metrics...'));
+
+ try {
+ const metrics = await this.collectMetrics();
+ const healthResults = await this.runHealthChecks();
+
+ this.baseline = {
+ timestamp: new Date().toISOString(),
+ healthScore: this.calculateHealthScore(healthResults),
+ topicCount: metrics.topics.length,
+ totalPartitions: metrics.topics.reduce((sum, topic) => sum + topic.partitions, 0),
+ consumerGroups: metrics.consumers.length,
+ totalLag: metrics.consumers.reduce((sum, consumer) => sum + consumer.totalLag, 0)
+ };
+
+ console.log(chalk.green('ā
Baseline established:'), {
+ healthScore: `${this.baseline.healthScore}%`,
+ topics: this.baseline.topicCount,
+ partitions: this.baseline.totalPartitions,
+ consumerGroups: this.baseline.consumerGroups,
+ totalLag: this.baseline.totalLag
+ });
+
+ } catch (error) {
+ console.error(chalk.yellow('ā ļø Failed to establish baseline:'), error.message);
+ }
+ }
+
+ startAnalysisInterval() {
+ this.intervalId = setInterval(async () => {
+ await this.runAnalysis();
+ }, this.monitoringConfig.interval * 1000);
+ }
+
+ startMetricsInterval() {
+ this.metricsIntervalId = setInterval(async () => {
+ await this.collectAndAnalyzeMetrics();
+ }, this.monitoringConfig.metricsInterval * 1000);
+ }
+
+ async runAnalysis() {
+ const timestamp = new Date().toISOString();
+ console.log(chalk.blue(`\nš Running analysis at ${timestamp}`));
+
+ try {
+ const metrics = await this.collectMetrics();
+ const healthResults = await this.runHealthChecks();
+ const currentHealthScore = this.calculateHealthScore(healthResults);
+
+ const analysis = this.performAnalysis(metrics, healthResults, currentHealthScore);
+
+ const report = {
+ timestamp,
+ type: 'continuous_analysis',
+ healthScore: currentHealthScore,
+ metrics,
+ healthResults,
+ analysis,
+ baseline: this.baseline
+ };
+
+ await this.saveReport(report);
+
+ this.displaySummary(analysis);
+
+ this.consecutiveFailures = 0;
+ this.lastHealthScore = currentHealthScore;
+ this.emit('analysis:complete', report);
+
+ } catch (error) {
+ this.consecutiveFailures++;
+ console.error(chalk.red(`ā Analysis failed (${this.consecutiveFailures}/${this.thresholds.consecutiveFailureLimit}):`), error.message);
+
+ if (this.consecutiveFailures >= this.thresholds.consecutiveFailureLimit) {
+ console.log(chalk.red('šØ Multiple consecutive failures detected - this may indicate a serious issue'));
+ this.emit('consecutive:failures', this.consecutiveFailures);
+ }
+
+ this.emit('analysis:error', error);
+ }
+ }
+
+ async collectAndAnalyzeMetrics() {
+ try {
+ const metrics = await this.collectQuickMetrics();
+ const issues = this.detectQuickIssues(metrics);
+
+ if (issues.length > 0) {
+ console.log(chalk.yellow(`ā ļø Quick check detected ${issues.length} potential issue(s):`));
+ issues.forEach(issue => console.log(chalk.yellow(` ⢠${issue}`)));
+ }
+
+ this.emit('metrics:collected', { timestamp: new Date().toISOString(), metrics, issues });
+
+ } catch (error) {
+ console.error(chalk.yellow('ā ļø Quick metrics collection failed:'), error.message);
+ }
+ }
+
+ async collectMetrics() {
+ console.log(chalk.gray('š Collecting cluster metrics...'));
+
+ const topics = await this.kafkaClient.getTopics();
+ const consumerGroups = await this.kafkaClient.getConsumerGroups();
+ const clusterInfo = await this.kafkaClient.admin.describeCluster();
+
+ const consumerMetrics = [];
+ for (const group of consumerGroups) {
+ try {
+ const groupDescription = await this.kafkaClient.admin.describeGroups([group.groupId]);
+ const offsets = await this.kafkaClient.admin.fetchOffsets({ groupId: group.groupId });
+
+ let totalLag = 0;
+ for (const offset of offsets) {
+ totalLag += Math.max(0, parseInt(offset.offset) || 0);
+ }
+
+ consumerMetrics.push({
+ groupId: group.groupId,
+ state: groupDescription.groups[0]?.state || 'Unknown',
+ memberCount: groupDescription.groups[0]?.members.length || 0,
+ totalLag
+ });
+
+ } catch (error) {
+ console.warn(chalk.yellow(`ā ļø Failed to get metrics for consumer group ${group.groupId}`));
+ }
+ }
+
+ return {
+ topics: topics.map(topic => ({
+ name: topic.name,
+ partitions: topic.partitions.length,
+ replicationFactor: topic.partitions[0]?.replicas.length || 0,
+ isInternal: topic.name.startsWith('__')
+ })),
+ consumers: consumerMetrics,
+ cluster: {
+ brokers: clusterInfo.brokers.length,
+ controller: clusterInfo.controller,
+ clusterId: clusterInfo.clusterId
+ }
+ };
+ }
+
+ async collectQuickMetrics() {
+ const consumerGroups = await this.kafkaClient.getConsumerGroups();
+
+ return {
+ consumerGroupCount: consumerGroups.length,
+ timestamp: new Date().toISOString()
+ };
+ }
+
+ async runHealthChecks() {
+ console.log(chalk.gray('š„ Running health checks...'));
+
+ const topics = await this.kafkaClient.getTopics();
+ const consumerGroups = await this.kafkaClient.getConsumerGroups();
+ const clusterInfo = await this.kafkaClient.admin.describeCluster();
+
+ return await this.healthChecker.runHealthChecks(clusterInfo, topics, consumerGroups);
+ }
+
+ calculateHealthScore(healthResults) {
+ // Handle both array format and object format from health checker
+ let checks = healthResults;
+
+ if (healthResults && typeof healthResults === 'object' && !Array.isArray(healthResults)) {
+ // If it's an object (from health checker), extract the checks array
+ checks = healthResults.checks || [];
+ }
+
+ if (!checks || !Array.isArray(checks) || checks.length === 0) return 0;
+
+ const passed = checks.filter(check => check.status === 'PASSED').length;
+ const total = checks.length;
+
+ return Math.round((passed / total) * 100);
+ }
+
+ performAnalysis(metrics, healthResults, currentHealthScore) {
+ const analysis = {
+ status: 'healthy',
+ issues: [],
+ improvements: [],
+ insights: [],
+ changesSinceBaseline: {}
+ };
+
+ if (this.baseline) {
+ analysis.changesSinceBaseline = this.compareWithBaseline(metrics, currentHealthScore);
+ }
+
+ if (this.lastHealthScore !== null) {
+ const healthChange = currentHealthScore - this.lastHealthScore;
+ if (Math.abs(healthChange) >= this.thresholds.healthScoreChange) {
+ const trend = healthChange > 0 ? 'improved' : 'degraded';
+ analysis.insights.push(`Health score ${trend} by ${Math.abs(healthChange)}% since last check`);
+
+ if (healthChange < 0) {
+ analysis.status = 'degraded';
+ analysis.issues.push(`Health score dropped from ${this.lastHealthScore}% to ${currentHealthScore}%`);
+ }
+ }
+ }
+
+ // Handle both array format and object format from health checker
+ let checks = healthResults;
+ if (healthResults && typeof healthResults === 'object' && !Array.isArray(healthResults)) {
+ checks = healthResults.checks || [];
+ }
+
+ const failedChecks = Array.isArray(checks) ? checks.filter(check => check.status === 'FAILED') : [];
+ if (failedChecks.length > 0) {
+ analysis.status = failedChecks.length > 2 ? 'critical' : 'warning';
+ analysis.issues.push(`${failedChecks.length} health check(s) failing`);
+ }
+
+ const totalLag = metrics.consumers.reduce((sum, consumer) => sum + consumer.totalLag, 0);
+ if (totalLag > 1000) {
+ analysis.issues.push(`High consumer lag detected: ${totalLag} messages`);
+ analysis.status = totalLag > 10000 ? 'critical' : 'warning';
+ }
+
+ const inactiveGroups = metrics.consumers.filter(c => c.memberCount === 0);
+ if (inactiveGroups.length > 0) {
+ analysis.improvements.push(`${inactiveGroups.length} inactive consumer group(s) could be cleaned up`);
+ }
+
+ if (analysis.issues.length === 0 && analysis.status === 'healthy') {
+ analysis.insights.push('All systems operating normally');
+ }
+
+ return analysis;
+ }
+
+ compareWithBaseline(metrics, currentHealthScore) {
+ if (!this.baseline) return {};
+
+ const changes = {};
+
+ const healthChange = currentHealthScore - this.baseline.healthScore;
+ if (healthChange !== 0) {
+ changes.healthScore = {
+ previous: this.baseline.healthScore,
+ current: currentHealthScore,
+ change: healthChange,
+ trend: healthChange > 0 ? 'improved' : 'degraded'
+ };
+ }
+
+ const topicChange = metrics.topics.length - this.baseline.topicCount;
+ if (topicChange !== 0) {
+ changes.topics = {
+ previous: this.baseline.topicCount,
+ current: metrics.topics.length,
+ change: topicChange
+ };
+ }
+
+ const currentTotalLag = metrics.consumers.reduce((sum, consumer) => sum + consumer.totalLag, 0);
+ const lagChange = currentTotalLag - this.baseline.totalLag;
+ if (Math.abs(lagChange) > 100) {
+ changes.consumerLag = {
+ previous: this.baseline.totalLag,
+ current: currentTotalLag,
+ change: lagChange,
+ trend: lagChange > 0 ? 'increased' : 'decreased'
+ };
+ }
+
+ return changes;
+ }
+
+ detectQuickIssues(metrics) {
+ const issues = [];
+
+ if (metrics.consumerGroupCount === 0) {
+ issues.push('No consumer groups detected');
+ }
+
+ return issues;
+ }
+
+ displaySummary(analysis) {
+ const statusEmoji = {
+ 'healthy': 'ā
',
+ 'warning': 'ā ļø',
+ 'degraded': 'š',
+ 'critical': 'šØ'
+ };
+
+ console.log(chalk.blue(`\nš Analysis Summary: ${statusEmoji[analysis.status]} ${analysis.status.toUpperCase()}`));
+
+ if (analysis.issues.length > 0) {
+ console.log(chalk.red('\nšØ Issues Detected:'));
+ analysis.issues.forEach(issue => console.log(chalk.red(` ⢠${issue}`)));
+ }
+
+ if (analysis.insights.length > 0) {
+ console.log(chalk.blue('\nš” Insights:'));
+ analysis.insights.forEach(insight => console.log(chalk.blue(` ⢠${insight}`)));
+ }
+
+ if (analysis.improvements.length > 0) {
+ console.log(chalk.yellow('\nš§ Potential Improvements:'));
+ analysis.improvements.forEach(improvement => console.log(chalk.yellow(` ⢠${improvement}`)));
+ }
+
+ if (Object.keys(analysis.changesSinceBaseline).length > 0) {
+ console.log(chalk.cyan('\nš Changes Since Baseline:'));
+ Object.entries(analysis.changesSinceBaseline).forEach(([key, change]) => {
+ const trend = change.trend || (change.change > 0 ? 'āļø' : 'āļø');
+ console.log(chalk.cyan(` ⢠${key}: ${change.previous} ā ${change.current} ${trend}`));
+ });
+ }
+ }
+
+ async saveReport(report) {
+ try {
+ const outputDir = this.config.file.outputDir;
+ await fs.mkdir(outputDir, { recursive: true });
+
+ const filename = `monitoring-${report.timestamp.replace(/[:.]/g, '-')}.json`;
+ const filepath = path.join(outputDir, filename);
+
+ await fs.writeFile(filepath, JSON.stringify(report, null, 2));
+ console.log(chalk.green(`š¾ Monitoring report saved: ${filename}`));
+
+ } catch (error) {
+ console.error(chalk.yellow('ā ļø Failed to save monitoring report:'), error.message);
+ }
+ }
+
+ async stop() {
+ if (!this.isRunning) {
+ console.log(chalk.yellow('ā ļø Monitoring is not running'));
+ return;
+ }
+
+ console.log(chalk.blue('š Stopping Continuous Kafka Monitoring...'));
+
+ if (this.intervalId) {
+ clearInterval(this.intervalId);
+ this.intervalId = null;
+ }
+
+ if (this.metricsIntervalId) {
+ clearInterval(this.metricsIntervalId);
+ this.metricsIntervalId = null;
+ }
+
+ try {
+ await this.kafkaClient.disconnect();
+ } catch (error) {
+ console.warn(chalk.yellow('ā ļø Error disconnecting Kafka client:'), error.message);
+ }
+
+ this.isRunning = false;
+ this.emit('stopped');
+
+ console.log(chalk.green('ā
Continuous monitoring stopped'));
+ }
+
+ getStatus() {
+ return {
+ isRunning: this.isRunning,
+ consecutiveFailures: this.consecutiveFailures,
+ lastHealthScore: this.lastHealthScore,
+ baseline: this.baseline ? {
+ establishedAt: this.baseline.timestamp,
+ healthScore: this.baseline.healthScore
+ } : null
+ };
+ }
+}
+
+module.exports = { ContinuousMonitor };