Skip to content

Log Router & Sinks Architecture

Tại sao Log Router là trung tâm của mọi quyết định

Log Router là thành phần bạn không thể bypass. Mọi log entry đi vào Cloud Logging đều phải qua Log Router trước khi đến bất kỳ destination nào. Hiểu Log Router là hiểu được tại sao:

  • Exclusion filter không giảm API quota
  • Một entry có thể xuất hiện ở nhiều destinations đồng thời
  • Aggregated sinks có thể chặn log của child resources
  • Cấu hình sai sink dẫn đến mất log không thể phục hồi

Internal Model: Log Router Architecture

Log Router là gì về mặt kỹ thuật

Log Router không phải một service riêng biệt mà là một component logic tích hợp trong Cloud Logging infrastructure. Mỗi resource level (project, billing account, folder, organization) có một Log Router instance riêng, xử lý các entries phát sinh tại resource đó.

Khi một log entry được nhận bởi Cloud Logging API, nó đi qua quá trình sau:

1. Reception: Entry đến Cloud Logging API endpoint
2. Validation: Timestamp check, size check, format validation
3. Buffering: Log Router tạm thời buffer entry
4. Sink evaluation: Mỗi sink trong resource được đánh giá độc lập
5. Routing: Entry được gửi đến tất cả sinks match (parallel)
6. Hierarchy traversal: Entry cũng được gửi lên aggregated sinks ở cấp cao hơn

Bước 4 là then chốt: mỗi sink evaluate entry độc lập. Một entry có thể match sink A, sink B, và sink C cùng một lúc — và nó sẽ được gửi đến tất cả ba destinations. Đây là cách log duplication xảy ra nếu bạn không cẩn thận.

Hierarchy traversal

Cloud Logging sử dụng resource hierarchy (project → folder → organization) để aggregate logs. Aggregated sinks ở cấp folder có thể nhận log từ tất cả projects trong folder đó.

Organization
├── Aggregated Sink: org-security-sink (BigQuery, centralized)

├── Folder: Engineering
│   ├── Aggregated Sink: eng-audit-sink (Cloud Storage)
│   │
│   ├── Project: backend-prod
│   │   ├── _Required sink (Admin Activity, System Event)
│   │   ├── _Default sink (tất cả còn lại)
│   │   └── Custom sink: backend-alerts (Pub/Sub)
│   │
│   └── Project: frontend-prod
│       ├── _Required sink
│       └── _Default sink

└── Folder: Data
    └── Project: analytics-prod
        ├── _Required sink
        └── _Default sink

Khi backend-prod ghi một Admin Activity log:

  1. _Required sink trong backend-prod route entry vào _Required bucket
  2. eng-audit-sink (aggregated, folder level) evaluate và route vào Cloud Storage
  3. org-security-sink (aggregated, org level) evaluate và route vào BigQuery

Kết quả: cùng một entry xuất hiện ở 3 destinations. Đây là thiết kế có chủ đích cho security và compliance use cases, nhưng cũng là nguyên nhân gây cost duplication nếu không được kiểm soát.


_Required và _Default Sinks

_Required Sink

_Required sink là built-in sink không thể modify hay delete. Nó có inclusion filter cứng:

log_id("cloudaudit.googleapis.com/activity") OR
log_id("cloudaudit.googleapis.com/system_event") OR
log_id("cloudaudit.googleapis.com/policy")

Đây là lý do Admin Activity, System Event và Policy Denied logs luôn xuất hiện trong _Required bucket — chúng match filter này. Access Transparency logs cũng route vào _Required.

Đặc tính quan trọng: Aggregated sinks với intercept_children = true không thể block entries đến _Required sink của child resources. _Required luôn nhận entries của nó, bất kể cấu hình aggregated sink.

_Default Sink

_Default sink nhận tất cả entries không match _Required filter:

NOT (
  log_id("cloudaudit.googleapis.com/activity") OR
  log_id("cloudaudit.googleapis.com/system_event") OR
  log_id("cloudaudit.googleapis.com/policy")
)

_Default sink có thể:

  • Modify: Bạn có thể thêm exclusion filters
  • Disable: Bạn có thể disable nó (mặc dù hiếm khi nên làm — sẽ mất nhiều logs)
  • Không thể delete: Như _Required, không xóa được

_Default sink route vào _Default bucket với retention 30 ngày. Đây là nơi hầu hết application logs và platform logs đi nếu không có custom sink nào.


Custom Sinks: Cơ Chế và Destinations

Anatomy của một Sink

Một sink có bốn thành phần:

Sink = {
  name: string,                    // Tên sink
  destination: string,             // Nơi gửi entries
  filter: string,                  // Inclusion filter (tùy chọn, không set = match all)
  exclusions: [ExclusionFilter],   // Danh sách exclusion conditions
  includeChildren: bool            // Chỉ cho aggregated sinks
}

Inclusion filter: Nếu không set, sink nhận mọi entry. Nếu set, chỉ nhận entries match filter. Đây là filter dương (positive filter).

Exclusion filters: Danh sách các điều kiện mà nếu entry match ANY, nó sẽ bị loại bỏ khỏi sink. Được evaluate sau inclusion filter. Đây là filter âm (negative filter).

Quá trình evaluate:

Entry vào → Match inclusion filter? → (No → bỏ qua)
                                      → (Yes) → Match bất kỳ exclusion? → (Yes → bỏ qua)
                                                                           → (No → route đến destination)

Destination Types

1. Log Buckets (trong Cloud Logging)

bash
gcloud logging sinks create my-sink \
  logging.googleapis.com/projects/my-project/locations/global/buckets/my-bucket \
  --log-filter='resource.type="k8s_container"'

Ưu điểm: Hỗ trợ Logs Explorer queries, log views, Log Analytics. Tích hợp native với Cloud Logging ecosystem. Nhược điểm: Giá storage sau 30 ngày, không phù hợp cho long-term archival ở scale lớn.

2. BigQuery Dataset

bash
gcloud logging sinks create bigquery-audit-sink \
  bigquery.googleapis.com/projects/my-project/datasets/audit_logs \
  --log-filter='log_id("cloudaudit.googleapis.com/activity")'

Ưu điểm: Truy vấn SQL mạnh, JOIN với dữ liệu khác, long-term retention giá rẻ, Analytics đỉnh cao. Nhược điểm: Không có real-time querying như Logs Explorer, cần biết SQL, table partitioning cần cấu hình.

BigQuery destination tự động tạo bảng với schema phản ánh LogEntry proto. Bảng được partitioned theo ngày (_PARTITIONTIME). Luôn query với partition filter để tránh full table scan tốn tiền:

sql
-- Đúng: sử dụng partition filter
SELECT protoPayload.authenticationInfo.principalEmail, COUNT(*) as ops_count
FROM `my-project.audit_logs.cloudaudit_googleapis_com_activity`
WHERE _PARTITIONTIME >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)
GROUP BY 1

-- Sai: không có partition filter → scan toàn bộ bảng
SELECT * FROM `my-project.audit_logs.cloudaudit_googleapis_com_activity`
WHERE jsonPayload.resource.type = 'gce_instance'

3. Cloud Storage Bucket

bash
gcloud logging sinks create gcs-archive-sink \
  storage.googleapis.com/my-log-archive-bucket \
  --log-filter='resource.type="gke_cluster"'

Log entries được lưu dưới dạng JSON files, organized theo YYYY/MM/DD/HH path. Cloud Storage là lựa chọn tốt nhất cho long-term archival vì giá rất thấp (Nearline/Coldline/Archive), nhưng không thể query trực tiếp — phải dùng Dataflow, Dataproc, hoặc BigQuery external table.

4. Pub/Sub Topic

bash
gcloud logging sinks create pubsub-alerts-sink \
  pubsub.googleapis.com/projects/my-project/topics/log-alerts \
  --log-filter='severity>=ERROR AND resource.type="k8s_container"'

Ưu điểm: Real-time streaming, tích hợp với Dataflow, functions, third-party SIEM (Splunk, Sumo Logic). Nhược điểm: Không lưu trữ (cần subscriber consume messages), cần quản lý subscriber và dead letter topics.

5. Logging Project (Cross-project routing)

Destination là một project khác — entries được gửi sang Log Router của project đó, và sẽ được evaluate bởi sinks của project đó. Có giới hạn one-hop: một entry được forward sang project khác một lần duy nhất, không tiếp tục forward sang project thứ ba.


Aggregated Sinks: Thu Thập Log Toàn Tổ Chức

Non-intercepting vs Intercepting

Đây là một trong những cơ chế ít được hiểu nhất của Log Router.

Non-intercepting aggregated sink (mặc định): Sink ở cấp folder/org nhận log từ child resources, nhưng log vẫn tiếp tục được evaluate bởi sinks trong child resources.

Project sinh log entry

     ├── Child project sinks (evaluate như bình thường)
     │   ├── _Required → _Required bucket của project
     │   └── _Default → _Default bucket của project

     └── Folder aggregated sink (NON-intercepting) cũng nhận entry
         └── Route vào BigQuery dataset của security team

Kết quả: Entry xuất hiện ở cả child project buckets VÀ folder BigQuery dataset.

Intercepting aggregated sink (includeChildren = true + sink ở cấp cao hơn configured thành intercept): Khi intercepting sink match một entry, child resources không nhận entry đó nữa (trừ _Required sink).

Intercepting là powerful nhưng nguy hiểm: nếu bạn tạo intercepting sink ở org level match mọi entries và route vào BigQuery, tất cả projects sẽ ngừng nhận logs vào _Default bucket của chúng. Developer không tìm thấy logs trong Logs Explorer nữa — chỉ tìm được trong BigQuery.

Khi nào dùng intercepting: Khi bạn muốn centralize log storage, tắt default per-project storage và chỉ giữ centralized copy. Thường dùng với mô hình centralized logging team với strict cost control.

Khi nào dùng non-intercepting: Khi bạn muốn thêm một destination mà không ảnh hưởng đến routing hiện tại của child resources.


Sink Evaluation: Thứ Tự và Semantics

Các entries được evaluate như thế nào

Khi một log entry đến Log Router, mọi sinks trong resource được evaluate đồng thời, song song. Không có thứ tự ưu tiên giữa các sinks — chúng độc lập với nhau.

Điều này có hệ quả quan trọng: không thể dùng một sink để "chặn" entry đến sink khác (trừ intercepting aggregated sink). Nếu bạn muốn entry A không đến _Default bucket, bạn phải thêm exclusion filter vào _Default sink, không phải tạo thêm sink mới.

Exclusion filters — hiểu đúng về post-ingestion

Một điểm rất quan trọng từ Google documentation: "Exclusion filters apply post-ingestion, so they cannot reduce API quota consumption."

Điều này nghĩa là:

  1. Entry vào Cloud Logging API → được tính vào API quota và cost
  2. Log Router evaluate sinks
  3. Exclusion filter trong sink quyết định entry có đến destination hay không
  4. Nếu exclusion filter loại bỏ entry, nó không đến destination nhưng vẫn đã được ingested và tính phí

Field exclusion (khác với sink exclusion filter) hoạt động khác: nó trim fields khỏi entry trước khi ghi vào bucket, do đó giảm được storage size nhưng không giảm được API write quota. Xem chi tiết ở File 04.


Sink Errors và Monitoring

Khi sink gặp lỗi (ví dụ: BigQuery dataset bị xóa, Pub/Sub topic không tồn tại, Cloud Storage bucket bị revoke permission), Cloud Logging:

  1. Ghi error notification vào _Default bucket của project đó
  2. Retry trong một khoảng thời gian
  3. Không lưu lại entries đã bị mất — entries trong thời gian lỗi bị drop

Đây là lý do monitoring sink health là bắt buộc. Metric quan trọng:

# Số entries bị drop do sink error
logging.googleapis.com/exports/error_count

Và luôn set IAM permissions đúng cho sink service account. Khi tạo sink, Cloud Logging tự động tạo một service account dạng serviceAccount:service-PROJECT_NUMBER@gcp-sa-logging.iam.gserviceaccount.com. Account này cần được grant write permissions trên destination.


Pattern Thực Tế: Centralized Security Logging

Đây là pattern phổ biến trong tổ chức lớn: một project riêng cho security team, nhận audit logs từ toàn bộ org.

bash
# Tạo aggregated sink ở org level, route vào security project
gcloud logging sinks create org-security-audit-sink \
  bigquery.googleapis.com/projects/security-project/datasets/org_audit_logs \
  --organization=123456789 \
  --include-children \
  --log-filter='log_id("cloudaudit.googleapis.com/activity") OR
                log_id("cloudaudit.googleapis.com/policy")'

# Grant BigQuery editor cho sink service account
# (Cloud logging sẽ print ra service account khi tạo sink)
bq add-iam-policy-binding security-project:org_audit_logs \
  --member="serviceAccount:service-123456789@gcp-sa-logging.iam.gserviceaccount.com" \
  --role="roles/bigquery.dataEditor"

Điều này tạo ra một aggregated, non-intercepting sink (entries vẫn đến _Required bucket của individual projects). Security team có thể query BigQuery dataset để cross-project audit analysis, trong khi individual teams vẫn thấy logs trong Logs Explorer.


References