ทำไมต้องใช้ ELK Stack จัดการ Log ของ WooCommerce
WooCommerce สร้าง log หลายประเภทเช่น order log, payment gateway log, shipping log, error log และ debug log ปัญหาคือ log เหล่านี้กระจายอยู่หลายที่ทั้งใน wp-content/debug.log, wc-logs directory และ database ทำให้ยากต่อการค้นหาและวิเคราะห์เมื่อเกิดปัญหา
ELK Stack ประกอบด้วย Elasticsearch สำหรับจัดเก็บและค้นหา log, Logstash สำหรับ parse และ transform log data และ Kibana สำหรับ visualize ข้อมูล เมื่อรวมกับ Filebeat ที่ทำหน้าที่เก็บ log จาก server แล้วส่งมายัง pipeline จะได้ระบบ centralized log management ที่ครบวงจร
ประโยชน์ของการใช้ ELK กับ WooCommerce ได้แก่ ค้นหา log ได้ภายในวินาทีแม้มี log หลายล้าน records, สร้าง dashboard แสดง real-time order analytics, ตั้ง alert เมื่อ payment failure เพิ่มขึ้นผิดปกติ, วิเคราะห์ performance bottleneck จาก slow query log และ ตรวจจับ security threats จาก access log
สถาปัตยกรรมของระบบประกอบด้วย WordPress/WooCommerce Server ที่ติดตั้ง Filebeat, Logstash Server ที่รับและ parse log, Elasticsearch Cluster ที่เก็บข้อมูล และ Kibana ที่แสดงผล
ติดตั้ง ELK Stack ด้วย Docker Compose
ติดตั้ง Elasticsearch, Logstash และ Kibana ด้วย Docker Compose สำหรับ production environment
# docker-compose-elk.yml
version: "3.9"
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.13.0
container_name: elasticsearch
environment:
- node.name=es01
- cluster.name=woocommerce-logs
- discovery.type=single-node
- bootstrap.memory_lock=true
- xpack.security.enabled=true
- xpack.security.enrollment.enabled=true
- ELASTIC_PASSWORD=
- "ES_JAVA_OPTS=-Xms2g -Xmx2g"
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- es_data:/usr/share/elasticsearch/data
ports:
- "9200:9200"
healthcheck:
test: ["CMD-SHELL", "curl -s -u elastic: http://localhost:9200/_cluster/health | grep -q green"]
interval: 30s
timeout: 10s
retries: 5
networks:
- elk
logstash:
image: docker.elastic.co/logstash/logstash:8.13.0
container_name: logstash
volumes:
- ./logstash/pipeline:/usr/share/logstash/pipeline
- ./logstash/config/logstash.yml:/usr/share/logstash/config/logstash.yml
ports:
- "5044:5044"
- "5000:5000"
environment:
- "LS_JAVA_OPTS=-Xms1g -Xmx1g"
- ELASTIC_PASSWORD=
depends_on:
elasticsearch:
condition: service_healthy
networks:
- elk
kibana:
image: docker.elastic.co/kibana/kibana:8.13.0
container_name: kibana
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
- ELASTICSEARCH_USERNAME=kibana_system
- ELASTICSEARCH_PASSWORD=
- xpack.security.enabled=true
ports:
- "5601:5601"
depends_on:
elasticsearch:
condition: service_healthy
networks:
- elk
volumes:
es_data:
networks:
elk:
driver: bridge
# .env
# ELASTIC_PASSWORD=your-elastic-password
# KIBANA_PASSWORD=your-kibana-password
# เริ่มต้นระบบ
# docker compose -f docker-compose-elk.yml up -d
# ตรวจสอบ Elasticsearch
# curl -u elastic:$ELASTIC_PASSWORD http://localhost:9200/_cluster/health?pretty
# ตั้งค่า kibana_system password
# curl -X POST -u elastic:$ELASTIC_PASSWORD \
# "http://localhost:9200/_security/user/kibana_system/_password" \
# -H "Content-Type: application/json" \
# -d '{"password":"your-kibana-password"}'
ตั้งค่า Filebeat เก็บ Log จาก WordPress และ WooCommerce
ติดตั้ง Filebeat บน WordPress Server เพื่อเก็บ log ทั้งหมดแล้วส่งไปยัง Logstash
# ติดตั้ง Filebeat บน Ubuntu
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo gpg --dearmor -o /usr/share/keyrings/elastic-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/elastic-keyring.gpg] https://artifacts.elastic.co/packages/8.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-8.x.list
sudo apt update && sudo apt install filebeat -y
# /etc/filebeat/filebeat.yml
filebeat.inputs:
# WordPress Debug Log
- type: log
id: wordpress-debug
enabled: true
paths:
- /var/www/html/wp-content/debug.log
fields:
log_type: wordpress_debug
multiline.pattern: '^\['
multiline.negate: true
multiline.match: after
# WooCommerce Logs
- type: log
id: woocommerce-logs
enabled: true
paths:
- /var/www/html/wp-content/uploads/wc-logs/*.log
fields:
log_type: woocommerce
multiline.pattern: '^\d{4}-\d{2}-\d{2}'
multiline.negate: true
multiline.match: after
# Apache/Nginx Access Log
- type: log
id: webserver-access
enabled: true
paths:
- /var/log/nginx/access.log
- /var/log/apache2/access.log
fields:
log_type: webserver_access
# Apache/Nginx Error Log
- type: log
id: webserver-error
enabled: true
paths:
- /var/log/nginx/error.log
- /var/log/apache2/error.log
fields:
log_type: webserver_error
# PHP-FPM Log
- type: log
id: php-fpm
enabled: true
paths:
- /var/log/php*-fpm.log
fields:
log_type: php_fpm
# MySQL Slow Query Log
- type: log
id: mysql-slow
enabled: true
paths:
- /var/log/mysql/mysql-slow.log
fields:
log_type: mysql_slow
multiline.pattern: '^# Time:|^# User@Host:'
multiline.negate: true
multiline.match: after
output.logstash:
hosts: ["logstash-server:5044"]
ssl.enabled: false
processors:
- add_host_metadata: ~
- add_fields:
target: ''
fields:
environment: production
site: woocommerce-shop
# เริ่มต้น Filebeat
sudo systemctl enable filebeat
sudo systemctl start filebeat
# ตรวจสอบสถานะ
sudo filebeat test config
sudo filebeat test output
sudo systemctl status filebeat
สร้าง Logstash Pipeline สำหรับ Parse WooCommerce Log
สร้าง Logstash Pipeline ที่ parse log จาก WooCommerce ให้เป็น structured data
# logstash/pipeline/woocommerce.conf
input {
beats {
port => 5044
}
}
filter {
# Parse WooCommerce Log
if [fields][log_type] == "woocommerce" {
grok {
match => {
"message" => "%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:log_level} %{GREEDYDATA:wc_message}"
}
}
date {
match => ["timestamp", "yyyy-MM-dd'T'HH:mm:ss", "yyyy-MM-dd HH:mm:ss"]
target => "@timestamp"
}
# Extract payment gateway info
if "payment" in [wc_message] or "PayPal" in [wc_message] or "Stripe" in [wc_message] {
mutate {
add_tag => ["payment"]
add_field => { "category" => "payment" }
}
grok {
match => {
"wc_message" => "Order #%{NUMBER:order_id}.*%{WORD:payment_gateway}"
}
tag_on_failure => []
}
}
# Extract order info
if "order" in [wc_message] {
grok {
match => {
"wc_message" => "Order #%{NUMBER:order_id}.*status changed from %{WORD:old_status} to %{WORD:new_status}"
}
tag_on_failure => []
}
}
}
# Parse WordPress Debug Log
if [fields][log_type] == "wordpress_debug" {
grok {
match => {
"message" => "\[%{HTTPDATE:timestamp}\] PHP %{DATA:php_error_type}: %{GREEDYDATA:error_message} in %{DATA:file} on line %{NUMBER:line_number}"
}
}
date {
match => ["timestamp", "dd/MMM/yyyy:HH:mm:ss Z"]
target => "@timestamp"
}
}
# Parse Nginx Access Log
if [fields][log_type] == "webserver_access" {
grok {
match => {
"message" => '%{IPORHOST:client_ip} - %{DATA:user} \[%{HTTPDATE:timestamp}\] "%{WORD:method} %{URIPATHPARAM:request} HTTP/%{NUMBER:http_version}" %{NUMBER:response_code} %{NUMBER:bytes} "%{DATA:referrer}" "%{DATA:user_agent}"'
}
}
date {
match => ["timestamp", "dd/MMM/yyyy:HH:mm:ss Z"]
target => "@timestamp"
}
# GeoIP lookup
geoip {
source => "client_ip"
target => "geoip"
}
# Identify WooCommerce API calls
if "/wp-json/wc/" in [request] or "/wc-api/" in [request] {
mutate {
add_tag => ["wc_api"]
add_field => { "category" => "wc_api" }
}
}
# Identify checkout pages
if "/checkout" in [request] or "/cart" in [request] {
mutate {
add_tag => ["checkout_flow"]
}
}
useragent {
source => "user_agent"
target => "ua"
}
}
# Parse MySQL Slow Query Log
if [fields][log_type] == "mysql_slow" {
grok {
match => {
"message" => "# Query_time: %{NUMBER:query_time} Lock_time: %{NUMBER:lock_time} Rows_sent: %{NUMBER:rows_sent} Rows_examined: %{NUMBER:rows_examined}"
}
}
mutate {
convert => {
"query_time" => "float"
"lock_time" => "float"
"rows_sent" => "integer"
"rows_examined" => "integer"
}
}
}
}
output {
elasticsearch {
hosts => ["http://elasticsearch:9200"]
user => "elastic"
password => ""
index => "woocommerce-%{[fields][log_type]}-%{+YYYY.MM.dd}"
}
}
สร้าง Kibana Dashboard สำหรับ WooCommerce Analytics
สร้าง Index Pattern และ Dashboard ใน Kibana
# สร้าง Index Pattern ผ่าน API
curl -X POST -u elastic:$ELASTIC_PASSWORD \
"http://localhost:5601/api/saved_objects/index-pattern/woocommerce-*" \
-H "kbn-xsrf: true" \
-H "Content-Type: application/json" \
-d '{
"attributes": {
"title": "woocommerce-*",
"timeFieldName": "@timestamp"
}
}'
# สร้าง ILM Policy สำหรับจัดการ index lifecycle
curl -X PUT -u elastic:$ELASTIC_PASSWORD \
"http://localhost:9200/_ilm/policy/woocommerce-log-policy" \
-H "Content-Type: application/json" \
-d '{
"policy": {
"phases": {
"hot": {
"actions": {
"rollover": {
"max_size": "10gb",
"max_age": "7d"
}
}
},
"warm": {
"min_age": "7d",
"actions": {
"shrink": {"number_of_shards": 1},
"forcemerge": {"max_num_segments": 1}
}
},
"cold": {
"min_age": "30d",
"actions": {
"freeze": {}
}
},
"delete": {
"min_age": "90d",
"actions": {
"delete": {}
}
}
}
}
}'
# Elasticsearch Queries สำหรับ WooCommerce Analytics
# 1. นับ Orders ต่อวัน
curl -s -u elastic:$ELASTIC_PASSWORD \
"http://localhost:9200/woocommerce-woocommerce-*/_search" \
-H "Content-Type: application/json" \
-d '{
"size": 0,
"query": {
"bool": {
"must": [
{"exists": {"field": "order_id"}},
{"match": {"new_status": "completed"}}
]
}
},
"aggs": {
"orders_per_day": {
"date_histogram": {
"field": "@timestamp",
"calendar_interval": "day"
}
}
}
}' | python3 -m json.tool
# 2. Top Payment Errors
curl -s -u elastic:$ELASTIC_PASSWORD \
"http://localhost:9200/woocommerce-woocommerce-*/_search" \
-H "Content-Type: application/json" \
-d '{
"size": 0,
"query": {
"bool": {
"must": [
{"term": {"category": "payment"}},
{"term": {"log_level": "ERROR"}}
]
}
},
"aggs": {
"by_gateway": {
"terms": {"field": "payment_gateway.keyword", "size": 10}
}
}
}'
ตั้งค่า Alert เมื่อพบ Error หรือ Suspicious Activity
ใช้ Elasticsearch Watcher หรือ Kibana Alerting สร้าง alert สำหรับ WooCommerce
# สร้าง Watcher Alert สำหรับ Payment Failure Spike
curl -X PUT -u elastic:$ELASTIC_PASSWORD \
"http://localhost:9200/_watcher/watch/payment_failure_alert" \
-H "Content-Type: application/json" \
-d '{
"trigger": {
"schedule": {"interval": "5m"}
},
"input": {
"search": {
"request": {
"indices": ["woocommerce-woocommerce-*"],
"body": {
"size": 0,
"query": {
"bool": {
"must": [
{"term": {"category": "payment"}},
{"term": {"log_level": "ERROR"}},
{"range": {"@timestamp": {"gte": "now-15m"}}}
]
}
}
}
}
}
},
"condition": {
"compare": {"ctx.payload.hits.total.value": {"gt": 10}}
},
"actions": {
"notify_slack": {
"webhook": {
"scheme": "https",
"host": "hooks.slack.com",
"port": 443,
"method": "post",
"path": "/services/xxx/yyy/zzz",
"body": "{\"text\":\"ALERT: {{ctx.payload.hits.total.value}} payment failures in last 15 minutes\"}"
}
}
}
}'
# Alert สำหรับ High Error Rate
curl -X PUT -u elastic:$ELASTIC_PASSWORD \
"http://localhost:9200/_watcher/watch/high_error_rate" \
-H "Content-Type: application/json" \
-d '{
"trigger": {
"schedule": {"interval": "10m"}
},
"input": {
"search": {
"request": {
"indices": ["woocommerce-webserver_access-*"],
"body": {
"size": 0,
"query": {
"bool": {
"must": [
{"range": {"response_code": {"gte": 500}}},
{"range": {"@timestamp": {"gte": "now-10m"}}}
]
}
}
}
}
}
},
"condition": {
"compare": {"ctx.payload.hits.total.value": {"gt": 50}}
},
"actions": {
"send_email": {
"email": {
"to": ["admin@example.com"],
"subject": "WooCommerce High 5xx Error Rate",
"body": {
"text": "{{ctx.payload.hits.total.value}} 5xx errors in last 10 minutes"
}
}
}
}
}'
FAQ คำถามที่พบบ่อย
Q: ELK Stack ต้องใช้ resource เท่าไหร่?
A: สำหรับ WooCommerce ขนาดเล็กถึงกลาง (ไม่เกิน 1000 orders/วัน) Elasticsearch ต้องการ RAM อย่างน้อย 4GB, Logstash 2GB และ Kibana 1GB รวมแล้ว server ควรมี RAM อย่างน้อย 8GB สำหรับ store ขนาดใหญ่อาจต้องการ Elasticsearch cluster หลาย node
Q: ใช้ Loki แทน ELK ได้ไหม?
A: ได้ Grafana Loki เป็นทางเลือกที่เบากว่ามาก ใช้ resource น้อยกว่า ELK และตั้งค่าง่ายกว่า เหมาะสำหรับ store ขนาดเล็กที่ไม่ต้องการ full-text search แต่ถ้าต้องการ complex query, aggregation และ visualization ที่หลากหลาย ELK ยังเป็นตัวเลือกที่ดีกว่า
Q: Filebeat กับ Fluentd ต่างกันอย่างไร?
A: Filebeat เขียนด้วย Go เบาและใช้ resource น้อย เหมาะเป็น log shipper ที่ส่ง log ไปยัง Logstash หรือ Elasticsearch ส่วน Fluentd เป็น log aggregator ที่มี plugin ecosystem ใหญ่กว่า สามารถ parse และ route log ได้เอง ถ้าใช้ ELK Stack แนะนำ Filebeat เพราะ integrate ได้ดีที่สุด
Q: ควรเก็บ WooCommerce log ไว้นานแค่ไหน?
A: ขึ้นอยู่กับข้อกำหนดทางกฎหมายและ storage budget โดยทั่วไปแนะนำเก็บ access log 90 วัน, error log 180 วัน และ payment log 1 ปี (ตาม PCI DSS requirement) ใช้ ILM Policy ของ Elasticsearch จัดการ lifecycle อัตโนมัติเพื่อย้ายข้อมูลเก่าไป cold storage หรือลบทิ้ง
