# თავი 11: ვებ აპლიკაციების მონიტორინგი (Monitoring Web Applications)
---
## 11.1 დეველოპმენტიდან პროდაქშენში (From Development to Production)
ამ კურსის განმავლობაში ჩვენ ვამოწმებდით ჩვენს აპლიკაციას ლოკალურად გაშვებით, გვერდებზე დაკლიკებით და კონსოლში ლოგების წაკითხვით. ეს არის დეველოპმენტი (development). პროდაქშენი (production) განსხვავებულია. პროდაქშენში თქვენ ვერ მიაერთებთ დებაგერს (debugger), ვერ დააკვირდებით კონსოლს და აპლიკაციას მხოლოდ თქვენ არ იყენებთ — მას იყენებს ათასობით მომხმარებელი ნებისმიერ დროს, და თქვენ უნდა იცოდეთ როდის მიდის რაღაც არასწორად, სანამ ისინი თავად გეტყვიან ამას.
ეს თავი წარმოგიდგენთ დაკვირვებადობას (observability) — ინსტრუმენტებსა და პრაქტიკებს, რომლებიც გაძლევთ საშუალებას გაიგოთ, რას აკეთებს თქვენი აპლიკაცია პროდაქშენში. ჩვენ ვისწავლით observability-ის სამ საყრდენს, როგორ დავამატოთ და დავაკონფიგუროთ Spring Boot Actuator, როგორ ვიმუშაოთ ჩაშენებულ health check-ებთან და metrics ენდფოინთებთან (endpoints), როგორ შევქმნათ custom health ინდიკატორები და ბიზნეს მეტრიკები Micrometer-ის გამოყენებით, როგორ დავიცვათ Actuator ენდფოინთები, როგორ დავნერგოთ სტრუქტურირებული ლოგირება (structured logging) პროდაქშენისთვის და როგორ მოვახდინოთ ინტეგრაცია Prometheus-სა და Grafana-სთან მონიტორინგის დაფებისთვის (dashboards).
---
## 11.2 Observability-ის სამი საყრდენი
Observability არის სისტემის შიდა მდგომარეობის გაგების უნარი მისი გარე შედეგების (outputs) შესწავლით. პროდაქშენში ეს შედეგები სამი ფორმით გვხვდება:
| საყრდენი (Pillar) | რას გეუბნებათ | მაგალითები |
| --- | --- | --- |
| **Logs (ლოგები)** | რა მოხდა — მოვლენების ტექსტური ჩანაწერები. | "Order 1234 created by user alice at 10:32:15." |
| **Metrics (მეტრიკები)** | რამდენი, რა სისწრაფით — რიცხვითი გაზომვები დროთა განმავლობაში. | Request-ების სიხშირე, შეცდომების რაოდენობა, მეხსიერების გამოყენება, პასუხის დაყოვნება (response latency). |
| **Traces (ტრეისები)** | გზა, რომელიც მოთხოვნამ (request) გაიარა სისტემაში. | ერთი მომხმარებლის მოთხოვნა, რომელიც გადის API gateway-ში, order სერვისში, payment სერვისში და მონაცემთა ბაზაში. |
**Logs (ლოგები)** ყველაზე ნაცნობია — ჩვენ ვიყენებდით `log.info()` და `log.error()`-ს მთელი ამ კურსის განმავლობაში. ისინი გვეუბნებიან რა მოხდა, მაგრამ მათი აგრეგაცია და ანალიზი მასშტაბურად რთულია სტრუქტურის გარეშე.
**Metrics (მეტრიკები)** არის რიცხვები. ისინი პასუხობენ კითხვებს, როგორიცაა "რამდენ მოთხოვნას ამუშავებს აპლიკაცია წამში?" და "როგორია პასუხის დროის (response time) 95-ე პროცენტილი?". მეტრიკების შეგროვება იაფია, მათზე alert-ების (გაფრთხილებების) დაყენება მარტივია და ისინი dashboard-ების საფუძველს წარმოადგენენ.
**Traces (ტრეისები)** აკავშირებენ წერტილებს განაწილებულ სისტემებში (distributed systems). როდესაც ერთი request ეხება მრავალ სერვისს, trace აკავშირებს მათ ერთმანეთთან, რათა დაინახოთ, სად დაიხარჯა დრო და სად მოხდა შეცდომები. Traces ყველაზე ღირებულია მიკროსერვისების (microservice) არქიტექტურებში.
ეს თავი ძირითადად ფოკუსირდება **მეტრიკებსა** და **health check-ებზე** Spring Boot Actuator-ის მეშვეობით, და ასევე **სტრუქტურირებულ ლოგირებაზე** პროდაქშენისთვის. Distributed tracing არის უფრო რთული თემა, რომელიც სცდება ამ კურსის ფარგლებს, თუმცა Spring Boot 4-ს აქვს მისი შესანიშნავი მხარდაჭერა OpenTelemetry ინტეგრაციის მეშვეობით.
---
## 11.3 Spring Boot Actuator-ის დამატება
Spring Boot Actuator არის მოდული, რომელიც ამატებს production-ready მონიტორინგის და მართვის ფუნქციებს თქვენს აპლიკაციაში. დაამატეთ dependency:
```xml
org.springframework.boot
spring-boot-starter-actuator
```
დამატების შემდეგ, Actuator აკეთებს ენდფოინთების ექსპოუზს (exposes endpoints) `/actuator/` გზაზე (path). ნაგულისხმევად (by default), მხოლოდ `/actuator/health` ენდფოინთია ხელმისაწვდომი HTTP-ით. სხვა ენდფოინთების ხელმისაწვდომობისთვის, თქვენ მკაფიოდ უნდა მიუთითოთ ისინი კონფიგურაციაში.
### საბაზისო კონფიგურაცია (Basic Configuration)
```properties
# კონკრეტული ენდფოინთების ექსპოუზი
management.endpoints.web.exposure.include=health,info,metrics,loggers
# ან ყველა ენდფოინთის ექსპოუზი (მხოლოდ development-ისთვის — არასოდეს production-ში)
management.endpoints.web.exposure.include=*
# Base path-ის შეცვლა (ნაგულისხმევი არის /actuator)
management.endpoints.web.base-path=/manage
# Actuator-ის გაშვება ცალკე პორტზე (გამოსადეგია მენეჯმენტ ტრაფიკის გამოსაყოფად)
management.server.port=9090
```
`management.endpoints.web.exposure.include` property აკონტროლებს, თუ რომელი ენდფოინთები იქნება ხელმისაწვდომი HTTP-ით. პროდაქშენში, გამოაჩინეთ (expose) მხოლოდ ის, რაც გჭირდებათ — ყოველი გამოჩენილი ენდფოინთი არის ინფორმაციის პოტენციური გაჟონვის (information leak) წყარო, თუ ის არ არის სათანადოდ დაცული.
---
## 11.4 ჩაშენებული Actuator ენდფოინთები
Actuator გვთავაზობს ენდფოინთების მდიდარ არჩევანს პირდაპირ ყუთიდან (out of the box):
| ენდფოინთი | აღწერა |
| --- | --- |
| `/actuator/health` | აპლიკაციის ჯანმრთელობის სტატუსი (UP, DOWN). |
| `/actuator/info` | აპლიკაციის ნებისმიერი მეტამონაცემი (metadata). |
| `/actuator/metrics` | აპლიკაციის მეტრიკები (მეხსიერება, CPU, HTTP მოთხოვნები და ა.შ.). |
| `/actuator/env` | გარემოს (environment) property-ები და კონფიგურაციის მნიშვნელობები. |
| `/actuator/beans` | ყველა Spring bean აპლიკაციის კონტექსტში (application context). |
| `/actuator/mappings` | ყველა `@RequestMapping` path-ები. |
| `/actuator/loggers` | ლოგის დონეების (log levels) ნახვა და შეცვლა runtime-ში. |
| `/actuator/threaddump` | JVM-ის thread dump. |
| `/actuator/heapdump` | Heap dump (იწერს ბინარულ ფაილს). |
| `/actuator/scheduledtasks` | ინფორმაცია დაგეგმილ ამოცანებზე (scheduled tasks). |
| `/actuator/caches` | აპლიკაციის ქეშები (caches). |
### /health ენდფოინთი
Health ენდფოინთი გვატყობინებს, მუშაობს თუ არა აპლიკაცია სწორად. Spring Boot ავტომატურად რთავს health check-ებს იმ კომპონენტებისთვის, რომლებსაც ის აღმოაჩენს — მონაცემთა ბაზა, დისკი, Redis, მეილ სერვერები და სხვა:
```json
{
"status": "UP",
"components": {
"db": {
"status": "UP",
"details": {
"database": "PostgreSQL",
"validationQuery": "isValid()"
}
},
"diskSpace": {
"status": "UP",
"details": {
"total": 499963174912,
"free": 250000000000
}
}
}
}
```
ნაგულისხმევად (by default), health ენდფოინთი აჩვენებს მხოლოდ ზედა დონის (top-level) სტატუსს (`UP` ან `DOWN`) კომპონენტების დეტალების გარეშე. სრული სურათის სანახავად:
```properties
management.endpoint.health.show-details=always
# ოფციები: never (ნაგულისხმევი), when-authorized, always
```
პროდაქშენში გამოიყენეთ `when-authorized` — მხოლოდ შესაბამისი როლის მქონე ავტორიზებულმა მომხმარებლებმა უნდა ნახონ შიდა health დეტალები.
Health ენდფოინთი არის **liveness და readiness probe-ების** საფუძველი კონტეინერების ორკესტრაციის სისტემებში, როგორიცაა Kubernetes. Kubernetes პერიოდულად იძახებს `/actuator/health`-ს რათა განსაზღვროს, გაუშვას თუ არა ტრაფიკი ინსტანციაზე ან გადატვირთოს (restart) ის.
### /info ენდფოინთი
Info ენდფოინთი აჩვენებს აპლიკაციის მეტამონაცემებს (metadata), რომლებსაც თქვენ თავად განსაზღვრავთ. შეავსეთ ის property-ების საშუალებით:
```properties
management.info.env.enabled=true
info.app.name=My Spring Boot App
info.app.description=A sample application for the course
info.app.version=1.0.0
```
ან ავტომატურად ჩართეთ build-ის ინფორმაცია Maven-იდან, `spring-boot-maven-plugin`-ში მიზნის (goal) დამატებით:
```xml
org.springframework.boot
spring-boot-maven-plugin
build-info
```
ეს აგენერირებს `build-info.properties` ფაილს build-ის დროს, რომელსაც Actuator კითხულობს ავტომატურად და აჩვენებს artifact-ის სახელს, ვერსიას, build-ის დროს და სხვა მეტამონაცემებს.
### /metrics ენდფოინთი
Metrics ენდფოინთი აჩვენებს ყველა ხელმისაწვდომი მეტრიკის სახელს. კონკრეტული მეტრიკის სანახავად, დაამატეთ მისი სახელი (append its name):
```
GET /actuator/metrics/jvm.memory.used
GET /actuator/metrics/http.server.requests
GET /actuator/metrics/system.cpu.usage
```
პასუხის მაგალითი (Example response):
```json
{
"name": "jvm.memory.used",
"description": "The amount of used memory",
"baseUnit": "bytes",
"measurements": [
{
"statistic": "VALUE",
"value": 1.5E8
}
]
}
```
Spring Boot ავტომატურად აგროვებს ათობით მეტრიკას — JVM მეხსიერება და garbage collection, CPU-ს გამოყენება, HTTP request-ების რაოდენობა და დაყოვნება (latencies), მონაცემთა ბაზის კონექშენ პულის (connection pool) სტატისტიკა და ქეშის hit/miss კოეფიციენტები. ეს ყველაფერი ხელმისაწვდომია მყისიერად, ყოველგვარი დამატებითი კოდის გარეშე.
### /loggers ენდფოინთი
Loggers ენდფოინთი გაძლევთ საშუალებას ნახოთ და **შეცვალოთ ლოგის დონეები (log levels) runtime-ში** აპლიკაციის გადატვირთვის (restart) გარეშე:
```bash
# ნახეთ მიმდინარე ლოგის დონე პაკეტისთვის
curl http://localhost:8080/actuator/loggers/com.example.myapp
# შეცვალეთ ის DEBUG-ზე
curl -X POST http://localhost:8080/actuator/loggers/com.example.myapp \
-H "Content-Type: application/json" \
-d '{"configuredLevel": "DEBUG"}'
```
ეს უკიდურესად სასარგებლოა პროდაქშენში პრობლემების დასადებაგებლად (debugging). თქვენ შეგიძლიათ დროებით ჩართოთ DEBUG ლოგირება კონკრეტული პაკეტისთვის, დააკვირდეთ დეტალურ output-ს და შემდეგ დააბრუნოთ ის INFO-ზე — ეს ყველაფერი ახალი დიფლოიმენტის (deployment) გარეშე.
---
## 11.5 Custom Health ინდიკატორები (Custom Health Indicators)
ჩაშენებული health check-ები ფარავს თავად Spring Boot-ის ინფრასტრუქტურას — მონაცემთა ბაზას, დისკს, მეილ სერვერს. მაგრამ თქვენი აპლიკაცია სავარაუდოდ დამოკიდებულია გარე სერვისებზე, რომელთა შესახებაც Spring Boot-მა არ იცის — third-party API, შეტყობინებების რიგი (message queue), ფაილების შენახვის სერვისი (file storage). თქვენ შეგიძლიათ შექმნათ custom health ინდიკატორები ამ დამოკიდებულებებისთვის (dependencies):
```java
@Component
public class ExternalApiHealthIndicator implements HealthIndicator {
private final RestClient restClient;
public ExternalApiHealthIndicator(RestClient restClient) {
this.restClient = restClient;
}
@Override
public Health health() {
try {
restClient.get()
.uri("https://api.example.com/health")
.retrieve()
.body(String.class);
return Health.up()
.withDetail("service", "External API")
.withDetail("status", "reachable")
.build();
} catch (Exception e) {
return Health.down()
.withDetail("service", "External API")
.withDetail("error", e.getMessage())
.build();
}
}
}
```
გაუკეთეთ იმპლემენტაცია `HealthIndicator` ინტერფეისს, დააბრუნეთ `Health.up()` როდესაც dependency ჯანმრთელია, და `Health.down()` დიაგნოსტიკური დეტალებით, როდესაც ის არ მუშაობს. Spring Boot ავტომატურად პოულობს იმ bean-ებს, რომლებიც აიმპლემენტირებენ `HealthIndicator`-ს და რთავს მათ `/actuator/health` პასუხში:
```json
{
"status": "UP",
"components": {
"externalApi": {
"status": "UP",
"details": {
"service": "External API",
"status": "reachable"
}
}
}
}
```
კომპონენტის სახელი (`externalApi`) გამომდინარეობს კლასის სახელიდან `HealthIndicator` სუფიქსის წაშლით და პირველი ასოს პატარა ასოთი (lowercase) ჩაწერით. თუ რომელიმე health ინდიკატორი აბრუნებს DOWN-ს, აპლიკაციის საერთო სტატუსი ხდება DOWN.
---
## 11.6 Custom მეტრიკები Micrometer-ით
**Micrometer** არის მეტრიკების ფასადი (facade), რომელსაც იყენებს Spring Boot — ისევე, როგორც SLF4J არის ფასადი ლოგირებისთვის. თქვენ წერთ მეტრიკების კოდს Micrometer-ის API-ს გამოყენებით, და Micrometer აგზავნის მონაცემებს იმ მონიტორინგის სისტემაში, რომელსაც აირჩევთ (Prometheus, Datadog, New Relic, InfluxDB და სხვა).
### მეტრიკის ტიპები (Metric Types)
| ტიპი (Type) | აღწერა | მაგალითი |
| --- | --- | --- |
| **Counter** | მნიშვნელობა, რომელიც მხოლოდ იზრდება. | განთავსებული შეკვეთების (orders) საერთო რაოდენობა. |
| **Gauge** | მნიშვნელობა, რომელიც შეიძლება გაიზარდოს ან შემცირდეს. | აქტიური სესიების მიმდინარე რაოდენობა. |
| **Timer** | ზომავს როგორც ხანგრძლივობას, ასევე რაოდენობას. | დრო, რომელიც დაიხარჯა თითოეული შეკვეთის დამუშავებაზე. |
| **Distribution Summary** | Timer-ის მსგავსია, მაგრამ არადროითი (non-time) მნიშვნელობებისთვის. | ატვირთული ფაილების ზომა. |
### Custom მეტრიკების შექმნა
გააკეთეთ `MeterRegistry`-ს ინჯექცია (inject) და შექმენით მეტრიკები კონსტრუქტორში:
```java
@Service
public class OrderService {
private final Counter orderCounter;
private final Timer orderTimer;
private final OrderRepository orderRepository;
public OrderService(MeterRegistry meterRegistry, OrderRepository orderRepository) {
this.orderRepository = orderRepository;
this.orderCounter = Counter.builder("orders.created.total")
.description("Total number of orders created")
.tag("type", "online")
.register(meterRegistry);
this.orderTimer = Timer.builder("orders.processing.time")
.description("Time to process an order")
.register(meterRegistry);
}
public Order createOrder(Order order) {
return orderTimer.record(() -> {
Order saved = orderRepository.save(order);
orderCounter.increment();
return saved;
});
}
}
```
Counter-ი აღრიცხავს თუ რამდენი შეკვეთა შეიქმნა. Timer-ი ზომავს, თუ რა დრო სჭირდება თითოეული შეკვეთის დამუშავებას. ორივე შეიცავს ტეგებს (tagged metadata), რომლებიც შეგიძლიათ გამოიყენოთ dashboard-ებში გასაფილტრად.
`orderTimer.record(() -> { ... })` ახვევს (wraps) ბიზნეს ლოგიკას — ის იწყებს ტაიმერს lambda-ს შესრულებამდე და აჩერებს მას შემდეგ, რითაც ავტომატურად ინახავს ხანგრძლივობას. Counter იზრდება lambda-ს შიგნით მხოლოდ მას შემდეგ, რაც შეკვეთა წარმატებით შეინახება.
### Gauge-ების გამოყენება
Gauge ზომავს მნიშვნელობას, რომელიც მერყეობს — აქტიური მომხმარებლები, რიგის (queue) სიღრმე, connection pool-ის ზომა:
```java
@Component
public class ActiveUsersMetric {
public ActiveUsersMetric(MeterRegistry meterRegistry,
SessionRegistry sessionRegistry) {
Gauge.builder("users.active", sessionRegistry,
registry -> registry.getAllPrincipals().size())
.description("Number of currently active users")
.register(meterRegistry);
}
}
```
Counter-ებისა და Timer-ებისგან განსხვავებით, Gauge არ ინახავს მნიშვნელობებს — ის კითხულობს მიმდინარე მნიშვნელობას წყაროდან (ამ შემთხვევაში `SessionRegistry`-დან) ყოველ ჯერზე, როდესაც მასზე მოთხოვნა იგზავნება.
### @Observed-ის გამოყენება ავტომატური ინსტრუმენტაციისთვის
Spring Boot 4 გვთავაზობს `@Observed` ანოტაციას, რომელიც ავტომატურად ქმნის ტაიმერს და ქაუნთერს მეთოდისთვის — ყოველგვარი ხელით დასაწერი `MeterRegistry` კოდის გარეშე:
```java
@Service
public class PaymentService {
@Observed(name = "payment.process")
public PaymentResult processPayment(String orderId) {
// Spring ავტომატურად აღრიცხავს:
// - payment.process.count (რამდენჯერ გამოიძახეს)
// - payment.process.duration (რა დრო დასჭირდა თითოეულ გამოძახებას)
return doProcessPayment(orderId);
}
}
```
ამისთვის საჭიროა `spring-boot-starter-aop` dependency და `ObservedAspect` bean. `@Observed` არის ყველაზე მარტივი გზა მეთოდის ინსტრუმენტაციისთვის, როდესაც არ გჭირდებათ custom ტეგები ან პირობითი (conditional) ლოგიკა.
### Custom მეტრიკების ნახვა
დარეგისტრირების შემდეგ, custom მეტრიკები გამოჩნდება მათ შესაბამის ენდფოინთებზე:
```
GET /actuator/metrics/orders.created.total
GET /actuator/metrics/orders.processing.time
```
---
## 11.7 Actuator ენდფოინთების უსაფრთხოება
Actuator-ის ენდფოინთებმა შეიძლება გამოაჩინოს (expose) სენსიტიური ინფორმაცია — გარემოს ცვლადები (environment variables, რომლებიც შეიძლება შეიცავდეს საიდუმლოებებს (secrets)), heap dump-ები (რომლებიც შეიცავს მეხსიერების მონაცემებს) და thread dump-ები (რომლებიც ამჟღავნებენ აპლიკაციის შიდა სტრუქტურას). პროდაქშენში ყოველთვის დაიცავით ისინი.
### შერჩევითი ექსპოუზი (Selective Exposure)
დაცვის პირველი ხაზი არის იმის შეზღუდვა, თუ რომელი ენდფოინთებია გამოჩენილი (exposed):
```properties
# მხოლოდ უსაფრთხო ენდფოინთების ექსპოუზი
management.endpoints.web.exposure.include=health,info,metrics,prometheus
# სენსიტიური ენდფოინთების მკაფიოდ გამორიცხვა (exclude)
management.endpoints.web.exposure.exclude=env,beans,heapdump
```
### დაცვა Spring Security-ით
მე-8 თავიდან ჩვენ ვიცით, როგორ დავაკონფიგუროთ URL-ზე დაფუძნებული წვდომის წესები. გამოიყენეთ ისინი Actuator-ის path-ებზე:
```java
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/health", "/actuator/info").permitAll()
.requestMatchers("/actuator/**").hasRole("ADMIN")
.anyRequest().authenticated()
);
return http.build();
}
```
ეს იძლევა საჯარო წვდომის (public access) საშუალებას `/health` და `/info`-ზე (რაც ჩვეულებრივ საჭიროა load balancer-ებისა და მონიტორინგის probe-ებისთვის), ხოლო ყველა სხვა Actuator ენდფოინთს ზღუდავს ადმინისტრატორებისთვის (administrators).
### ცალკე პორტზე გაშვება (Running on a Separate Port)
გავრცელებული პროდაქშენ პატერნი არის Actuator-ის გაშვება განსხვავებულ პორტზე, რომელიც არ არის ხელმისაწვდომი საჯარო ინტერნეტისთვის:
```properties
# აპლიკაცია 8080 პორტზე (public-facing)
server.port=8080
# Actuator 9090 პორტზე (მხოლოდ შიდა ქსელისთვის)
management.server.port=9090
```
აპლიკაცია ემსახურება მომხმარებლების ტრაფიკს 8080 პორტზე, ხოლო მონიტორინგის ინსტრუმენტები წვდებიან Actuator-ს 9090 პორტზე. Firewall ბლოკავს გარე წვდომას 9090 პორტზე — მხოლოდ შიდა მონიტორინგის ინფრასტრუქტურას შეუძლია მასთან დაკავშირება. ეს უფრო მარტივია, ვიდრე ენდფოინთების ავთენტიფიკაციით დაცვა და წარმოადგენს ფართოდ გამოყენებულ მიდგომას.
---
## 11.8 სტრუქტურირებული ლოგირება (Structured Logging)
დეველოპმენტში ადამიანისთვის წაკითხვადი ლოგის ხაზები მისაღებია:
```
2025-01-15 10:30:45 INFO com.example.OrderService - Order created: id=1234
```
პროდაქშენში ამ ხაზებს მოიხმარენ ლოგების აგრეგაციის სისტემები — ELK stack (Elasticsearch, Logstash, Kibana), Grafana Loki, AWS CloudWatch, ან მსგავსი ინსტრუმენტები. ამ სისტემებს ესაჭიროებათ მანქანისთვის წაკითხვადი (machine-readable) ფორმატები. **სტრუქტურირებული ლოგირება (Structured logging)** გამოიტანს ლოგებს JSON ობიექტების სახით, რაც აადვილებს მათ დაპარსვას (parse), ძიებასა და გაფილტვრას.
### ჩაშენებული სტრუქტურირებული ლოგირება
Spring Boot-ს აქვს ჩაშენებული მხარდაჭერა სტრუქტურირებული ლოგირებისთვის. ერთი property ცვლის კონსოლის output-ს JSON-ად:
```properties
logging.structured.format.console=ecs
```
Spring Boot მხარს უჭერს სამ სტრუქტურირებულ ფორმატს: `ecs` (Elastic Common Schema), `logstash` და `gelf` (Graylog Extended Log Format). აირჩიეთ ის, რომელსაც ელოდება თქვენი ლოგების აგრეგაციის სისტემა.
### Logstash Logback Encoder-ის გამოყენება
მეტი კონტროლისთვის გამოიყენეთ Logstash Logback Encoder ბიბლიოთეკა:
```xml
net.logstash.logback
logstash-logback-encoder
7.4
```
დააკონფიგურირეთ ის `logback-spring.xml`-ში:
```xml
requestId
userId
%d{HH:mm:ss} %-5level %logger{36} - %msg%n
```
`` ელემენტი მე-5 თავის პროფილების სისტემიდან ირჩევს ლოგირების კონფიგურაციას აქტიური პროფილის მიხედვით. დეველოპმენტი იღებს წაკითხვად, ფერად კონსოლის output-ს. პროდაქშენი იღებს სტრუქტურირებულ JSON-ს.
JSON ლოგის output-ის მაგალითი:
```json
{
"@timestamp": "2025-01-15T10:30:45.123Z",
"level": "INFO",
"logger_name": "com.example.OrderService",
"message": "Order created successfully",
"thread_name": "http-nio-8080-exec-1",
"requestId": "abc-123-def",
"userId": "user42"
}
```
### კონტექსტის დამატება MDC-ით
**MDC (Mapped Diagnostic Context)** ამაგრებს კონტექსტურ მონაცემებს — მოთხოვნის ID-ებს (request IDs), მომხმარებლის ID-ებს, სესიის ID-ებს — თითოეულ ლოგის შეტყობინებასთან (log message), რომელიც გენერირდება request-ის დროს. ეს გაძლევთ საშუალებას გაფილტროთ ყველა ლოგი კონკრეტული request-ისთვის ან მომხმარებლისთვის თქვენს აგრეგაციის სისტემაში.
```java
@Component
public class RequestIdFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
try {
String requestId = UUID.randomUUID().toString();
MDC.put("requestId", requestId);
chain.doFilter(request, response);
} finally {
MDC.clear();
}
}
}
```
ფილტრი აგენერირებს უნიკალურ request ID-ს, ინახავს მას MDC-ში სანამ მოთხოვნა დამუშავდება და ასუფთავებს მას დამუშავების შემდეგ. ყოველი `log.info()`, `log.error()`, ან `log.debug()` გამოძახება ამ მოთხოვნის ფარგლებში ავტომატურად შეიცავს `requestId`-ს სტრუქტურირებულ output-ში. პრობლემის გამოძიებისას, თქვენ ეძებთ request ID-ს და იღებთ ყველა ლოგის შეტყობინებას ამ კონკრეტული მოთხოვნიდან, ყველა კლასსა და ფენაში (layer).
---
## 11.9 Prometheus-ისა და Grafana-ს ინტეგრაცია
Actuator-ის ენდფოინთები სასარგებლოა დროებითი (ad-hoc) ინსპექტირებისთვის, მაგრამ უწყვეტი მონიტორინგისთვის დაგჭირდებათ სისტემა, რომელიც აგროვებს მეტრიკებს დროთა განმავლობაში და ახდენს მათ ვიზუალიზაციას. **Prometheus** არის time-series მონაცემთა ბაზა, რომელიც პერიოდულად აგროვებს (scrapes) მეტრიკებს თქვენი აპლიკაციიდან. **Grafana** არის ვიზუალიზაციის ინსტრუმენტი, რომელიც აგზავნის მოთხოვნებს (queries) Prometheus-ში და აგენერირებს dashboard-ებს.
### Prometheus-ის მხარდაჭერის დამატება
დაამატეთ Micrometer Prometheus registry:
```xml
io.micrometer
micrometer-registry-prometheus
```
დააექსპოუზეთ (Expose) Prometheus-ის ენდფოინთი:
```properties
management.endpoints.web.exposure.include=health,info,metrics,prometheus
```
ეს ქმნის `/actuator/prometheus` ენდფოინთს, რომელსაც გამოაქვს მეტრიკები Prometheus-ის ტექსტურ ფორმატში:
```
# HELP jvm_memory_used_bytes The amount of used memory
# TYPE jvm_memory_used_bytes gauge
jvm_memory_used_bytes{area="heap",id="G1 Eden Space"} 2.5165824E7
jvm_memory_used_bytes{area="heap",id="G1 Old Gen"} 1.8874368E7
# HELP http_server_requests_seconds Duration of HTTP server request handling
# TYPE http_server_requests_seconds summary
http_server_requests_seconds_count{method="GET",status="200",uri="/products"} 42
http_server_requests_seconds_sum{method="GET",status="200",uri="/products"} 1.234
```
თითოეული ჩაშენებული მეტრიკა და თითოეული custom მეტრიკა, რომელსაც შექმნით Micrometer-ით, ავტომატურად კეთდება export ამ ენდფოინთის მეშვეობით.
### Prometheus-ის კონფიგურაცია
Prometheus-ს ესაჭიროება კონფიგურაციის ფაილი, რომელიც ეუბნება, თუ საიდან უნდა მოაგროვოს (scrape) მონაცემები:
```yaml
# prometheus.yml
scrape_configs:
- job_name: 'spring-boot-app'
metrics_path: '/actuator/prometheus'
scrape_interval: 15s
static_configs:
- targets: ['localhost:8080']
```
Prometheus იძახებს `/actuator/prometheus`-ს ყოველ 15 წამში, პარსავს მეტრიკებს და ინახავს მათ თავის time-series მონაცემთა ბაზაში. თქვენ შემდეგ შეგიძლიათ გამოიძახოთ (query) ეს მონაცემები PromQL-ის (Prometheus Query Language) გამოყენებით, რათა უპასუხოთ კითხვებს, როგორიცაა "რამდენია საშუალო response time ბოლო 5 წუთის განმავლობაში?" ან "რამდენი 500 შეცდომა მოხდა ბოლო ერთ საათში?".
### Grafana Dashboards
Grafana უკავშირდება Prometheus-ს, როგორც მონაცემთა წყაროს (data source) და გაძლევთ საშუალებას ააწყოთ ვიზუალური დაფები (dashboards). Grafana.com-ზე ხელმისაწვდომია წინასწარ აწყობილი dashboard-ები Spring Boot აპლიკაციებისთვის (მაგალითად, dashboard ID 4701), რომლებიც ვიზუალურად აჩვენებენ JVM მეხსიერების გამოყენებას, CPU-ს გამოყენებას, HTTP მოთხოვნების სიხშირესა და დაყოვნებას, შეცდომების სიხშირეს, garbage collection-ის აქტივობას და თქვენს custom ბიზნეს მეტრიკებს.
დეველოპმენტში Prometheus-ისა და Grafana-ს დაყენება როგორც წესი ხდება Docker Compose-ის საშუალებით:
```yaml
# docker-compose.yml (monitoring stack)
services:
prometheus:
image: prom/prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
grafana:
image: grafana/grafana
ports:
- "3000:3000"
```
Prometheus-ისა და Grafana-ს დაყენების დეტალური აღწერა სცდება ამ თავის ფარგლებს, მაგრამ ინტეგრაცია Spring Boot-ის მხრიდან არის უბრალოდ dependency-ის დამატება და ენდფოინთის ექსპოუზი — დანარჩენი ყველაფერი ხდება მონიტორინგის ინფრასტრუქტურაში.
---
## 11.10 ყველაფრის ერთად თავმოყრა (Putting It All Together)
აქვეა პროდაქშენ აპლიკაციისთვის მონიტორინგის სრული კონფიგურაცია, რომელიც აერთიანებს ყველაფერს ამ თავიდან:
### application.properties
```properties
# Actuator ენდფოინთები
management.endpoints.web.exposure.include=health,info,metrics,loggers,prometheus
management.endpoint.health.show-details=when-authorized
management.info.env.enabled=true
# Application info
info.app.name=My Application
info.app.version=1.0.0
info.app.environment=production
# სტრუქტურირებული ლოგირება (production)
logging.structured.format.console=ecs
```
### Custom Health ინდიკატორი
```java
@Component
public class PaymentGatewayHealthIndicator implements HealthIndicator {
private final RestClient restClient;
public PaymentGatewayHealthIndicator(RestClient restClient) {
this.restClient = restClient;
}
@Override
public Health health() {
try {
restClient.get()
.uri("https://payments.example.com/health")
.retrieve()
.body(String.class);
return Health.up()
.withDetail("gateway", "reachable")
.build();
} catch (Exception e) {
return Health.down()
.withDetail("gateway", "unreachable")
.withDetail("error", e.getMessage())
.build();
}
}
}
```
### სერვისი Custom მეტრიკებით
```java
@Service
public class ProductService {
private static final Logger log = LoggerFactory.getLogger(ProductService.class);
private final ProductRepository productRepository;
private final Counter productViewCounter;
public ProductService(ProductRepository productRepository,
MeterRegistry meterRegistry) {
this.productRepository = productRepository;
this.productViewCounter = Counter.builder("products.views.total")
.description("Total product page views")
.register(meterRegistry);
}
public Product findById(Long id) {
productViewCounter.increment();
log.info("Product viewed: id={}", id);
return productRepository.findById(id)
.orElseThrow(() -> new ProductNotFoundException("Product not found: " + id));
}
}
```
### უსაფრთხოების კონფიგურაცია Actuator-ისთვის
```java
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/health", "/actuator/info").permitAll()
.requestMatchers("/actuator/**").hasRole("ADMIN")
.anyRequest().authenticated()
);
return http.build();
}
```
---
## შეჯამება (Summary)
ამ თავში განვიხილეთ, თუ როგორ გავხადოთ Spring Boot აპლიკაცია დაკვირვებადი (observable) და production-ready.
* **Observability-ის სამი საყრდენია** ლოგები (რა მოხდა), მეტრიკები (რამდენი და რა სისწრაფით) და ტრეისები (რა გზა გაიარა მოთხოვნამ). ერთად აღებული, ისინი გაძლევენ სრულ სურათს თქვენი აპლიკაციის ქცევის შესახებ პროდაქშენში.
* **Spring Boot Actuator** ამატებს მონიტორინგის ენდფოინთებს თქვენს აპლიკაციაში. ჩაშენებული ენდფოინთები მოიცავს `/health` (აპლიკაციის სტატუსი), `/info` (მეტამონაცემები), `/metrics` (რიცხვითი გაზომვები), `/loggers` (ლოგის დონეების ნახვა და შეცვლა runtime-ში) და მრავალ სხვას.
* **Custom health ინდიკატორები** აიმპლემენტირებენ `HealthIndicator` ინტერფეისს გარე დამოკიდებულებების (external dependencies) — third-party API-ების, შეტყობინებების რიგების (message queues), payment gateway-ების — მონიტორინგისთვის. ისინი ავტომატურად ჩნდებიან `/health` პასუხში.
* **Micrometer** არის მეტრიკების ფასადი (facade). **Counters (მთვლელები)** აღრიცხავენ კუმულაციურ ჯამებს. **Gauges** ზომავენ მერყევ მნიშვნელობებს. **Timers (ტაიმერები)** ზომავენ როგორც ხანგრძლივობას, ასევე რაოდენობას. `@Observed` ანოტაცია Spring Boot 4-ში უზრუნველყოფს ავტომატურ ინსტრუმენტაციას ხელით დასაწერი `MeterRegistry` კოდის გარეშე.
* **Actuator-ის დაცვა** გულისხმობს შერჩევით ექსპოუზს (მხოლოდ იმ ენდფოინთების, რომლებიც გჭირდებათ), Spring Security წესებს (როლებზე დაფუძნებული წვდომა) და, სურვილისამებრ, Actuator-ის გაშვებას ცალკე პორტზე, რომელიც არ არის საჯაროდ ხელმისაწვდომი.
* **სტრუქტურირებული ლოგირება (Structured logging)** გამოიტანს ლოგებს JSON-ის სახით მანქანური მოხმარებისთვის. Spring Boot-ის ჩაშენებული `logging.structured.format.console` property რთავს ამას ერთი ხაზით. **MDC** ამაგრებს კონტექსტურ მონაცემებს (request ID-ებს, user ID-ებს) თითოეულ ლოგის შეტყობინებაზე მარტივი ფილტრაციისთვის და ტრეისინგისთვის.
* **Prometheus** აგროვებს (scrapes) მეტრიკებს `/actuator/prometheus` ენდფოინთიდან და ინახავს მათ დროითი სერიების (time series) სახით. **Grafana** ახდენს ამ მეტრიკების ვიზუალიზაციას dashboard-ების სახით, რაც უზრუნველყოფს აპლიკაციის ჯანმრთელობის, პერფორმანსის და ბიზნეს მეტრიკების real-time შეფასებას.
---
## რესურსები (Resources)
* [Spring Boot Actuator — Reference Documentation](https://docs.spring.io/spring-boot/reference/actuator/index.html)
* [Micrometer Documentation](https://micrometer.io/docs)
* [Structured Logging — Spring Boot Reference](https://docs.spring.io/spring-boot/reference/features/logging.html#features.logging.structured)
* [Prometheus Documentation](https://prometheus.io/docs/introduction/overview/)
* [Grafana Documentation](https://grafana.com/docs/)
* [Baeldung: Spring Boot Actuator](https://www.baeldung.com/spring-boot-actuators)
---
## პრაქტიკული დავალება (Lab Assignment): მონიტორინგის დამატება თქვენს ვებ აპლიკაციაში
მოახდინეთ მონიტორინგის შესაძლებლობების ინტეგრაცია თქვენს არსებულ აპლიკაციაში.
**მოთხოვნები:**
1. **დაამატეთ Spring Boot Actuator** და დააექსპოუზეთ `health`, `info`, `metrics` და `loggers` ენდფოინთები.
2. **დააკონფიგურირეთ `/info` ენდფოინთი** თქვენი აპლიკაციის სახელით, ვერსიითა და აღწერით — property-ების ან Maven-ის `build-info` მიზნის (goal) გამოყენებით.
3. **შექმენით custom health ინდიკატორი**, რომელიც ამოწმებს გარე დამოკიდებულების (external dependency) სტატუსს, რომელსაც იყენებს თქვენი აპლიკაცია (მე-10 თავის third-party API, ან მონაცემთა ბაზის health check-ი custom query-ით).
4. **დაამატეთ მინიმუმ ორი custom მეტრიკა** Micrometer-ის გამოყენებით: **Counter** ბიზნეს ივენთის აღსარიცხად (მაგ. მომხმარებლის რეგისტრაციები, პროდუქტის ნახვები, შეკვეთები) და **Timer** კონკრეტული ოპერაციის ხანგრძლივობის გასაზომად (მაგ. ძიების მოთხოვნის დრო, API გამოძახების ხანგრძლივობა).
5. **დაიცავით Actuator ენდფოინთები** ისე, რომ `/health` და `/info` იყოს საჯაროდ ხელმისაწვდომი, ხოლო ყველა სხვა ენდფოინთი ითხოვდეს `ADMIN` როლს.
6. **დააკონფიგურირეთ სტრუქტურირებული ლოგირება** production პროფილისთვის (JSON ფორმატი) და ამავდროულად შეინარჩუნეთ ადამიანისთვის წაკითხვადი ლოგები development პროფილისთვის. გამოიყენეთ კონკრეტულ პროფილზე მორგებული კონფიგურაცია (profile-specific configuration), როგორც ეს განვიხილეთ მე-5 თავში.