Athena 查詢 Production ALB Logs 完整指南
更新日期:2026-07-27
目的
這份手冊用來獨立完成:
- 確認 Production ALB logs 是否有 90 天資料。
- 使用 Athena SQL 統計各 API 的 7、30、90 天呼叫量。
- 區分 GET、POST、OPTIONS、狀態碼、target group 與 listener rule。
- 找出仍有正式流量但尚未搬移的 legacy API。
- 找出 90 天零流量、可進一步評估不搬的 API。
- 控制 Athena 掃描量、費用與敏感資料輸出。
這份手冊不修改 Load Balancer、不切換流量,也不修改 S3 原始 ALB logs。
快速使用地圖
第一次操作時依序閱讀:
- 第四節:把權限申請文字交給管理員。
- 第五節:權限開通後確認 workgroup、database、table。
- 第六節:只有找不到既有 table 時,才處理建表。
- 第九節:先跑一天並確認 Data scanned。
- 第十一節:確認 90 天資料是否存在。
- 第十三節:一次產出 7、30、90 天流量。
- 第十八、十九節:匯出 CSV 並和程式 inventory 合併。
遇到錯誤直接看第二十一節;每次重跑前使用第二十三節 checklist。
一、先理解 Athena
Athena 是直接查詢 S3 檔案的 Serverless SQL 服務。
S3 原始 ALB .log.gz
↓
Glue Data Catalog table
只保存欄位 schema、S3 location、partition 規則
↓
Athena SELECT
↓
S3 query resultAthena 不是另一套資料庫,不會先把 ALB logs 搬進 Athena。
SELECT 不會修改來源 log,但每次查詢會:
- 讀取 S3 source objects。
- 產生 Athena 掃描費用。
- 把 query result 寫入指定的 S3 result location。
CREATE TABLE 只會建立 Glue metadata;CTAS、INSERT、UNLOAD 等操作可能另外寫入 S3。本流程預設只允許 CREATE EXTERNAL TABLE 與 SELECT。
二、目前已確認的實際環境
| 項目 | Production | Staging |
|---|---|---|
| AWS profile | default | default |
| Region | ap-northeast-1 | ap-northeast-1 |
| ALB name | pro360-api | ALB-PRO360-Staging |
| ALB resource id | app/pro360-api/d3898ad14c394f67 | app/ALB-PRO360-Staging/22d9e3275ff14d23 |
| S3 bucket | pro360-alb-logs | 待另行確認 |
| S3 prefix | web | 待另行確認 |
Production access log:
enabled = true
bucket = pro360-alb-logs
prefix = web實際驗證:
- 2026-07-27 找到 240 個 pro360-api log objects。
- 已下載一個約 741 KB 的 gzip log。
- 已成功解析 request method 與 URL path。
- 同一個 web prefix 也包含 ALB-PRO360,因此查詢必須限制 Production ALB resource id。
- 90 天歷史資料是否完整仍待確認。
目前權限狀態:
| Profile | IAM user | Athena |
|---|---|---|
| default | matt | AccessDenied |
| meite | mei-te | AccessDenied |
| meitehk | 另一個 HK 帳號 | 不使用 |
三、固定安全規則
每次 Athena 查詢都遵守:
- 必須有 day 日期條件。
- 必須限制 elb = app/pro360-api/d3898ad14c394f67。
- 第一次先查一天,確認 Data scanned 後才擴大到 7、30、90 天。
- OPTIONS 分開統計,不混入 business request。
- 不輸出 query string。
- 原始 URL、IP、ARN、user agent 明細不放進 git。
- 不使用 DROP、DELETE、INSERT、UPDATE、CTAS。
- LIMIT 不是成本控制;沒有 day filter 時仍可能掃描大量資料。
- 使用有 per-query data usage limit 的專用 workgroup。
四、管理員一次性開通
4.1 需要什麼
這次以「可獨立完成 ALB log 分析工作」為目標。權限分成三層,不要全部混成一次申請:
管理員必做:
- 掛載 AWS managed policy AmazonAthenaFullAccess。
- Production ALB log S3 prefix 的 List/Get 權限。
開通後可自行處理:
- 建立名稱符合 aws-athena-query-results-* 的 result bucket。
- 建立與調整 Athena workgroup、scan limit。
- 建立與維護 Glue database、table、partition。
條件式處理:
- 只有 S3 location 受 Lake Formation 管理,才補 Lake Formation grants。
- 只有 source/result 使用 customer-managed KMS key,才補 KMS 權限。
- 只有使用自訂名稱的 result bucket,才另補 result bucket policy。
AmazonAthenaFullAccess 已包含:
- athena 全功能。
- Glue database/table/partition 的 create、read、update、delete。
- lakeformation:GetDataAccess。
- CloudWatch query metrics 與 alarm 相依權限。
- 名稱符合 aws-athena-query-results-* 的結果 bucket 基本權限。
它不會自動提供 Production ALB source object 的 s3:GetObject;自訂名稱的 result bucket 也要另外授權。
4.2 權限申請文字
可直接交給 AWS 管理員:
目的:
獨立建立與維護 Athena/Glue ALB log 分析環境,
查詢 pro360-api 的 90 天 ALB access logs,
產生並更新 API migration 流量盤點。
Region:
ap-northeast-1
Source:
s3://pro360-alb-logs/web/AWSLogs/<ACCOUNT_ID>/elasticloadbalancing/ap-northeast-1/
管理員必做:
1. 掛載 AWS managed policy:
arn:aws:iam::aws:policy/AmazonAthenaFullAccess
2. Source S3 prefix:
ListBucket、GetBucketLocation、GetObject。
開通後由 matt 自行處理:
1. 建立 aws-athena-query-results-* result bucket。
2. 建立專用 workgroup 並設定 per-query scan limit。
3. 建立 Athena/Glue database、table 與 partition projection。
只有確認環境有使用時才處理:
1. 若 S3 location 由 Lake Formation 管理:
- catalog/database:DESCRIBE
- database:CREATE_TABLE、ALTER
- table:SELECT、DESCRIBE、ALTER、DROP
- source location:DATA_LOCATION_ACCESS
2. 若 source/result 使用 customer-managed KMS key:
kms:DescribeKey、kms:Decrypt、kms:Encrypt、
kms:GenerateDataKey、kms:ReEncrypt*
3. 若使用非 aws-athena-query-results-* 的自訂 result bucket,
才另給該 bucket/prefix 的 List/Get/Put/Delete。
不需要:
ELB 修改、IAM 使用者/角色管理、S3 source Put/Delete。另外建立必要的 Source S3 policy;以下 <ACCOUNT_ID> 由管理員替換:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ListAlbLogSource",
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:GetBucketLocation"
],
"Resource": "arn:aws:s3:::pro360-alb-logs",
"Condition": {
"StringLike": {
"s3:prefix": [
"web/AWSLogs/<ACCOUNT_ID>/elasticloadbalancing/ap-northeast-1",
"web/AWSLogs/<ACCOUNT_ID>/elasticloadbalancing/ap-northeast-1/*"
]
}
}
},
{
"Sid": "ReadAlbLogSource",
"Effect": "Allow",
"Action": [
"s3:GetObject"
],
"Resource": "arn:aws:s3:::pro360-alb-logs/web/AWSLogs/<ACCOUNT_ID>/elasticloadbalancing/ap-northeast-1/*"
}
]
}Source S3 只允許 List/Get。標準 aws-athena-query-results-* bucket 的建立與結果讀寫,已由 AmazonAthenaFullAccess 處理。
若組織有 SCP、permission boundary、S3 bucket policy、KMS key policy 或 Lake Formation explicit deny,IAM Allow 不會覆蓋這些限制。管理員需一次檢查這五層,不能只確認 IAM user policy。
4.3 Workgroup 建議
建議建立專用 workgroup,例如:
pro360-alb-analysis設定:
- Enforce workgroup configuration。
- 固定 query result S3 location。
- 開啟 result encryption。
- 第一次設定較小的 per-query scan limit。
- 看過一天實際 Data scanned 後再調整 90 天上限。
- 開啟 CloudWatch query metrics。
4.4 管理員 AWS Console 操作
以下操作都先確認右上角 AWS account 是正式台灣帳號,Region 是 Tokyo / ap-northeast-1。
A. 掛載 AmazonAthenaFullAccess
- 開啟 IAM Console。
- 左側選 Users。
- 選使用者 matt。
- 開啟 Permissions。
- 選 Add permissions。
- 選 Attach policies directly。
- 搜尋 AmazonAthenaFullAccess。
- 勾選後選 Next,再選 Add permissions。
- 回到 matt 的 Permissions,確認 policy 已出現。
- 同頁檢查 Permissions boundary;若存在,確認它允許 Athena、Glue、S3、Lake Formation 與 KMS。
若公司統一用 IAM group 或 role,將同樣 policy 掛到 group/role,再讓 matt 使用該身分。
B. 準備 Athena result bucket
掛上 AmazonAthenaFullAccess 後,本段可由 matt 自行完成;不必要求管理員先建立。
- 開啟 S3 Console。
- 先找是否已有 Tokyo Region 的 Athena result bucket。
- 若沒有,選 Create bucket。
- 建議名稱:aws-athena-query-results-<ACCOUNT_ID>-ap-northeast-1。
- Region 選 ap-northeast-1。
- 保持 Block all public access。
- 開啟 default encryption;SSE-S3 最簡單,SSE-KMS 則繼續做下面 KMS 步驟。
- 建議使用 prefix:pro360-alb-analysis/。
- 建立 lifecycle rule,只清理 pro360-alb-analysis/,例如 90 天後刪除 result。
然後建立前述自訂 S3 policy:
- IAM Console 左側選 Policies。
- 選 Create policy。
- 選 JSON。
- 貼上 4.2 的 S3 policy。
- 將 ACCOUNT_ID 換成實際值。
- 選 Next。
- Policy name 建議 Pro360AlbAthenaS3Access。
- 建立後回到 Users > matt > Permissions > Add permissions。
- 掛上 Pro360AlbAthenaS3Access。
C. 建立專用 Athena workgroup
掛上 AmazonAthenaFullAccess 後,本段也可由 matt 自行完成。
- 開啟 Athena Console。
- 左側選 Workgroups。
- 選 Create workgroup。
- Name 填 pro360-alb-analysis。
- Analytics engine 選 Athena SQL。
- Query result location 填:
s3://<ATHENA_RESULT_BUCKET>/pro360-alb-analysis/ - 開啟 Override client-side settings / Enforce workgroup configuration。
- 開啟 query result encryption。
- 開啟 Publish query metrics to CloudWatch。
- 設定 per-query data usage control;初始可設 50 GB,實測一天與 90 天掃描量後再調整。
- 建立 workgroup。
- 進入 Query editor 時切換到 pro360-alb-analysis,不使用 primary。
使用 workgroup 強制 result location,可以避免 Console、CLI 使用不同輸出位置。
D. 檢查 Lake Formation
- 開啟 Lake Formation Console。
- 左側找 Data lake locations。
- 搜尋 pro360-alb-logs 或對應 S3 ARN。
- 若找不到,表示此 location 未受 Lake Formation location registration 管理,可跳過本段。
- 若有註冊,進入 Permissions > Data permissions > Grant。
- Principal 選 IAM user matt。
- 若 pro360_log_analysis 尚未建立,在 Catalog 層授予 CREATE_DATABASE 與 DESCRIBE。
- 若 database 已存在,對 pro360_log_analysis 授予 Super;不需要 Grantable permissions,除非 matt 要再授權別人。
- 到 Permissions > Data locations > Grant。
- Principal 選 matt。
- Location 選包含 pro360-alb-logs 的已註冊位置。
- 授予 DATA_LOCATION_ACCESS。
- 若分析既有 table,再對該 table 或 database 下所有 tables 授予 SELECT、DESCRIBE。
IAM policy 和 Lake Formation grant 是兩層檢查;只完成其中一層仍可能 AccessDenied。
E. 檢查 KMS
- 在 Source bucket 與 Result bucket 的 Properties 查看 Default encryption。
- 若任一 bucket 使用 AWS KMS key,記下 Key ARN。
- 開啟 KMS Console。
- 選該 customer-managed key。
- 在 Key users 或 Key policy 加入 matt 使用的 IAM user/role。
- 確認允許 DescribeKey、Decrypt、Encrypt、GenerateDataKey、ReEncrypt。
- 若是 AWS managed key,依該服務限制確認是否可直接使用。
F. 管理員最後檢查
管理員完成前一次確認:
- IAM user/group/role policy。
- Permissions boundary。
- AWS Organizations SCP。
- Source/result S3 bucket policy。
- KMS key policy。
- Lake Formation grants。
- Athena workgroup result location 與 scan limit。
任一層有 explicit Deny,都會蓋過 IAM Allow。
Athena 超過 per-query limit 時會取消查詢,但取消前已掃描的資料仍可能計費。
五、權限開通後的第一輪檢查
5.1 確認身分
aws sts get-caller-identity \
--profile default \
--no-cli-pager確認是台灣正式 AWS account。
5.2 列出 workgroups
aws athena list-work-groups \
--profile default \
--region ap-northeast-1 \
--query 'WorkGroups[].{Name:Name,State:State}' \
--output table \
--no-cli-pager確認管理員指定的 workgroup 是 ENABLED。
5.3 查看 workgroup
export MIG_ATHENA_WORKGROUP='pro360-alb-analysis'
aws athena get-work-group \
--profile default \
--region ap-northeast-1 \
--work-group "$MIG_ATHENA_WORKGROUP" \
--output json \
--no-cli-pager確認:
- ResultConfiguration.OutputLocation。
- EnforceWorkGroupConfiguration。
- EncryptionConfiguration。
- BytesScannedCutoffPerQuery 或管理員設定的 data controls。
5.4 列出 databases
aws athena list-databases \
--profile default \
--region ap-northeast-1 \
--catalog-name AwsDataCatalog \
--query 'DatabaseList[].Name' \
--output table \
--no-cli-pager5.5 列出 tables
假設 database 名稱是 pro360_log_analysis:
export MIG_ATHENA_DATABASE='pro360_log_analysis'
aws athena list-table-metadata \
--profile default \
--region ap-northeast-1 \
--catalog-name AwsDataCatalog \
--database-name "$MIG_ATHENA_DATABASE" \
--query 'TableMetadataList[].Name' \
--output table \
--no-cli-pager若已有 ALB table,先使用既有 table,不重複建立。
六、尚無 table 時如何建立
6.1 建議做法
使用 AWS 官方的 ALB access log partition projection DDL:
AWS:Create ALB access log table with partition projection
不要自行縮短官方欄位或刪除 RegexSerDe 最後的 optional pattern。AWS 可能在 ALB log 尾端增加新欄位,官方 pattern 已保留向後相容空間。
6.2 固定替換值
複製官方 DDL 後替換:
| 官方 placeholder | 此環境 |
|---|---|
| table name | pro360_alb_logs |
| bucket | pro360-alb-logs |
| prefix | web |
| account | 執行 sts get-caller-identity 取得 |
| region | ap-northeast-1 |
| partition column | day |
| day format | yyyy/MM/dd |
| day range start | 依 S3 最舊保留日設定 |
LOCATION:
s3://pro360-alb-logs/web/AWSLogs/<ACCOUNT_ID>/elasticloadbalancing/ap-northeast-1/storage.location.template:
s3://pro360-alb-logs/web/AWSLogs/<ACCOUNT_ID>/elasticloadbalancing/ap-northeast-1/${day}確認官方 DDL 至少包含下列 partition projection properties;起始日要換成 S3 實際最舊保留日:
projection.enabled = true
projection.day.type = date
projection.day.range = <oldest-yyyy/MM/dd>,NOW
projection.day.format = yyyy/MM/dd
projection.day.interval = 1
projection.day.interval.unit = DAYS
storage.location.template = .../${day}建議 table:
Database:pro360_log_analysis
Table:pro360_alb_logs
Partition projection:enabledPartition projection 啟用後,不需要每天 ALTER TABLE ADD PARTITION,也不需要 MSCK REPAIR TABLE。
6.3 建表的影響
CREATE EXTERNAL TABLE:
- 寫入 Glue schema metadata。
- 不搬移 S3 log。
- 不修改 S3 source log。
- 不修改 ALB。
建表仍是 AWS metadata write,需 reviewer/admin 明確同意。
七、建議使用 Athena Console
第一次操作建議使用 AWS Console:
- 登入 AWS Console。
- Region 切到 Tokyo / ap-northeast-1。
- 開啟 Athena。
- 選管理員指定的 workgroup。
- Data source 選 AwsDataCatalog。
- Database 選 pro360_log_analysis。
- Table 選 pro360_alb_logs。
- 先跑一天的 sanity query。
- 在 Query statistics 查看 Data scanned。
- 確認正確後再執行 7、30、90 天 query。
不要一開始就跑 90 天。
八、SQL 固定值
以下 SQL 假設:
Database:pro360_log_analysis
Table:pro360_alb_logs
Production elb:app/pro360-api/d3898ad14c394f67日期 partition 使用 UTC:
day = yyyy/MM/dd台灣日期跨 UTC 邊界時,需同時查相鄰 UTC day,再用 timestamp 轉 Asia/Taipei 過濾。
九、第一個低成本查詢
只查一天,確認資料與 parser:
SELECT
count(*) AS total_requests,
min(from_iso8601_timestamp(time)) AS first_request_utc,
max(from_iso8601_timestamp(time)) AS last_request_utc
FROM pro360_log_analysis.pro360_alb_logs
WHERE day = '2026/07/27'
AND elb = 'app/pro360-api/d3898ad14c394f67';執行後記錄:
Query execution ID
Data scanned
Engine version
Workgroup
Result location若 total_requests = 0:
- 檢查 day 格式是不是 yyyy/MM/dd。
- 檢查 elb 使用 slash 格式,不是 object filename 的 dot 格式。
- 暫時只保留 day filter,查看 DISTINCT elb。
- 檢查 table LOCATION 是否包含 web/AWSLogs。
十、安全查看欄位
只輸出必要欄位並移除 query string:
SELECT
from_iso8601_timestamp(time) AS request_time_utc,
request_verb,
regexp_extract(
request_url,
'^https?://[^/]+([^?]*)',
1
) AS request_path,
elb_status_code,
target_status_code,
regexp_extract(
target_group_arn,
'targetgroup/([^/]+)',
1
) AS target_group,
matched_rule_priority
FROM pro360_log_analysis.pro360_alb_logs
WHERE day = '2026/07/27'
AND elb = 'app/pro360-api/d3898ad14c394f67'
LIMIT 20;這個 LIMIT 只限制輸出,不取代 day filter。
十一、確認 90 天資料是否存在
先查起點、終點與中間日期:
SELECT
day,
count(*) AS requests
FROM pro360_log_analysis.pro360_alb_logs
WHERE day IN (
'2026/04/29',
'2026/06/01',
'2026/07/27'
)
AND elb = 'app/pro360-api/d3898ad14c394f67'
GROUP BY day
ORDER BY day;判讀:
- 三天都有:可以繼續做 90 天查詢。
- 最舊日期為 0:查更晚日期,找出實際 retention 起點。
- 中間日期為 0:檢查 log delivery 或 table partition 設定。
零筆不能直接推論該日完全無流量。
十二、API path normalization
ALB log 會出現實際 ID:
/quote_bids/change_status/read/2604664734.json
/quote_service_categories/add/quote_service_id:9930/quote_category_id:700.json盤點時要轉成:
/quote_bids/change_status/read/{id}.json
/quote_service_categories/add/quote_service_id:{id}/quote_category_id:{id}.jsonNormalization query:
WITH base AS (
SELECT
day,
time,
request_verb,
regexp_extract(
request_url,
'^https?://[^/]+([^?]*)',
1
) AS raw_path,
elb_status_code,
target_status_code,
target_group_arn,
matched_rule_priority
FROM pro360_log_analysis.pro360_alb_logs
WHERE day BETWEEN '2026/07/21' AND '2026/07/27'
AND elb = 'app/pro360-api/d3898ad14c394f67'
AND request_verb <> 'OPTIONS'
),
normalized AS (
SELECT
*,
regexp_replace(
regexp_replace(
regexp_replace(
raw_path,
'/[0-9]+(/|\.json|$)',
'/{id}$1'
),
'([A-Za-z_]+:)[0-9]+',
'$1{id}'
),
'/[0-9a-fA-F]{8}-[0-9a-fA-F-]{27}([/]|$)',
'/{uuid}$1'
) AS normalized_path
FROM base
)
SELECT
request_verb,
normalized_path,
count(*) AS calls,
max(from_iso8601_timestamp(time)) AS last_seen_utc
FROM normalized
GROUP BY request_verb, normalized_path
ORDER BY calls DESC;這只是通用 normalization。匯出後仍需抽查:
- CakePHP named parameters。
- 非數字 business keys。
- locale/version path。
- 空 segment 與多餘斜線。
- 同一路徑但不同 method。
不要把會改變行為的 named parameter 整段刪掉。
十三、一次產出 7、30、90 天統計
以下範例以 2026-07-27 為統計終點。執行時更新日期:
WITH base AS (
SELECT
day,
time,
request_verb,
regexp_extract(
request_url,
'^https?://[^/]+([^?]*)',
1
) AS raw_path,
elb_status_code,
target_status_code,
target_group_arn,
matched_rule_priority
FROM pro360_log_analysis.pro360_alb_logs
WHERE day BETWEEN '2026/04/29' AND '2026/07/27'
AND elb = 'app/pro360-api/d3898ad14c394f67'
),
normalized AS (
SELECT
*,
regexp_replace(
regexp_replace(
regexp_replace(
raw_path,
'/[0-9]+(/|\.json|$)',
'/{id}$1'
),
'([A-Za-z_]+:)[0-9]+',
'$1{id}'
),
'/[0-9a-fA-F]{8}-[0-9a-fA-F-]{27}([/]|$)',
'/{uuid}$1'
) AS normalized_path
FROM base
)
SELECT
request_verb,
normalized_path,
sum(
CASE
WHEN day BETWEEN '2026/07/21' AND '2026/07/27'
THEN 1 ELSE 0
END
) AS calls_7d,
sum(
CASE
WHEN day BETWEEN '2026/06/28' AND '2026/07/27'
THEN 1 ELSE 0
END
) AS calls_30d,
count(*) AS calls_90d,
count_if(elb_status_code BETWEEN 200 AND 299) AS status_2xx,
count_if(elb_status_code BETWEEN 400 AND 499) AS status_4xx,
count_if(elb_status_code BETWEEN 500 AND 599) AS status_5xx,
max(from_iso8601_timestamp(time)) AS last_seen_utc,
max_by(
regexp_extract(
target_group_arn,
'targetgroup/([^/]+)',
1
),
from_iso8601_timestamp(time)
) AS latest_target_group,
max_by(
matched_rule_priority,
from_iso8601_timestamp(time)
) AS latest_rule_priority
FROM normalized
GROUP BY request_verb, normalized_path
ORDER BY calls_90d DESC;注意:
- 上面會依 method 分組,因此 OPTIONS 會是獨立的列。
- 只要 business request 時,在
baseCTE 加上AND request_verb <> 'OPTIONS'。 - 90 天日期要以 UTC day 計算,若以台灣日界線為準需加 timestamp filter。
- Query statistics 的 Data scanned 要記錄。
十四、只看 legacy API
常見 legacy route 不以 /new/v1 開頭。初步篩選:
WITH paths AS (
SELECT
request_verb,
regexp_extract(
request_url,
'^https?://[^/]+([^?]*)',
1
) AS request_path
FROM pro360_log_analysis.pro360_alb_logs
WHERE day BETWEEN '2026/04/29' AND '2026/07/27'
AND elb = 'app/pro360-api/d3898ad14c394f67'
AND request_verb <> 'OPTIONS'
)
SELECT
request_verb,
request_path,
count(*) AS calls
FROM paths
WHERE request_path NOT LIKE '/new/v1/%'
GROUP BY request_verb, request_path
ORDER BY calls DESC;這只是初步篩選,不代表所有非 /new/v1 都是 getlancer legacy。最終仍需和 legacy/new code inventory 比對。
十五、確認實際導向哪個 Target Group
SELECT
request_verb,
regexp_extract(
request_url,
'^https?://[^/]+([^?]*)',
1
) AS request_path,
regexp_extract(
target_group_arn,
'targetgroup/([^/]+)',
1
) AS target_group,
matched_rule_priority,
count(*) AS calls
FROM pro360_log_analysis.pro360_alb_logs
WHERE day BETWEEN '2026/07/21' AND '2026/07/27'
AND elb = 'app/pro360-api/d3898ad14c394f67'
GROUP BY
request_verb,
regexp_extract(request_url, '^https?://[^/]+([^?]*)', 1),
regexp_extract(target_group_arn, 'targetgroup/([^/]+)', 1),
matched_rule_priority
ORDER BY calls DESC;這份結果用來交叉驗證 listener-rules.json,不只依賴規則設定推測流量。
十六、Latency 與尖峰
p50/p95/p99 target latency
SELECT
approx_percentile(target_processing_time, 0.50) AS p50_seconds,
approx_percentile(target_processing_time, 0.95) AS p95_seconds,
approx_percentile(target_processing_time, 0.99) AS p99_seconds
FROM pro360_log_analysis.pro360_alb_logs
WHERE day BETWEEN '2026/07/21' AND '2026/07/27'
AND elb = 'app/pro360-api/d3898ad14c394f67'
AND target_processing_time >= 0;Peak requests/minute
SELECT
date_trunc(
'minute',
from_iso8601_timestamp(time)
) AS minute_utc,
count(*) AS requests
FROM pro360_log_analysis.pro360_alb_logs
WHERE day BETWEEN '2026/07/21' AND '2026/07/27'
AND elb = 'app/pro360-api/d3898ad14c394f67'
GROUP BY 1
ORDER BY requests DESC
LIMIT 100;容量評估應用 peak requests/minute 或 RPS,不用 API 數量判斷是否加機器。
十七、Caller 分類
User-Agent 分類只能作近似判斷:
SELECT
CASE
WHEN lower(user_agent) LIKE '%mozilla/%' THEN 'web'
WHEN regexp_like(
lower(user_agent),
'alamofire|cfnetwork|ios'
) THEN 'ios'
WHEN regexp_like(
lower(user_agent),
'okhttp|android'
) THEN 'android'
ELSE 'internal_or_unknown'
END AS caller,
count(*) AS calls
FROM pro360_log_analysis.pro360_alb_logs
WHERE day BETWEEN '2026/07/21' AND '2026/07/27'
AND elb = 'app/pro360-api/d3898ad14c394f67'
GROUP BY 1
ORDER BY calls DESC;分類規則要用實際 User-Agent 樣本校正,不可直接當絕對結論。
十八、匯出 CSV
Athena Console:
- 等 Query status 變成 Succeeded。
- 先記錄 Query execution ID 與 Data scanned。
- 在 Results 選 Download CSV。
- 存到非 git evidence 資料夾。
建議檔名:
production_api_traffic_2026-04-29_to_2026-07-27.csv同時建立摘要:
Environment
ALB resource id
UTC date range
Athena database/table
Workgroup
Query execution ID
Data scanned
Query result location
Normalization version
Known limitations十九、如何產生 Migration Gap
Athena 只回答 Production 流量,不知道程式是否已搬。
最後要合併:
Legacy code inventory
+ New code/route inventory
+ Production listener rules
+ Athena production traffic
= api_gap_inventory.csv狀態:
| 狀態 | 判斷 |
|---|---|
| MIGRATED_LIVE | New code + production rule/target + production evidence |
| MIGRATED_NOT_LIVE | New code 存在,但 production 仍走 legacy |
| LEGACY_ACTIVE_GAP | Legacy 有 90 天流量,但 new code/route 缺少 |
| LEGACY_DORMANT_CANDIDATE | 90 天觀測為 0,仍待其他 caller 確認 |
| NO_MIGRATION_APPROVED | Reviewer/product 明確核准不搬 |
零流量不能直接等於不搬。
二十、成本控制
Athena on-demand SQL 主要依 Data scanned 計價。AWS 公開範例使用每 TB 5 USD。
成本估算:
Estimated query cost
= Data scanned TB × Athena region price per TB降低成本:
- 一定加 day filter。
- 第一次只查一天。
- 使用 partition projection。
- 使用 gzip source。
- 不重複執行相同大查詢。
- 使用 query result reuse。
- Workgroup 設 per-query limit。
- 將大範圍統計合併成一次 90 天 scan,不分別重掃 7、30、90 天。
目前 web prefix 是多 ALB 共用。elb filter 對正確性是必要的,但資料仍存放在共用日期目錄,實際 Data scanned 以 Athena Query statistics 為準。
除了 Athena,還有:
- S3 LIST/GET request。
- Query result S3 storage/request。
- Glue Data Catalog。
- KMS,若 result/source 使用 KMS encryption。
二十一、常見錯誤
AccessDenied:athena:ListDatabases
Athena metadata 權限缺少。目前 default、meite 都是此狀態。
AccessDenied:Glue
已有 Athena 權限,但缺 Glue GetDatabase/GetTable/GetPartitions。
Unable to verify/create output bucket
Query result location 未設定,或缺 result bucket 權限。
Query succeeded but returns 0
檢查:
- day 是 yyyy/MM/dd。
- elb 使用 app/pro360-api/d3898ad14c394f67。
- Table LOCATION 包含 web/AWSLogs。
- S3 該 UTC 日期是否有物件。
HIVE_BAD_DATA 或欄位全部 null
RegexSerDe 和目前 ALB log 格式不一致。回到 AWS 官方最新 DDL,不自行刪除尾端 optional pattern。
Data scanned 異常大
通常是:
- 缺 day filter。
- Partition projection 設定錯。
- Table LOCATION 指到過大的 bucket root。
- 誤以為 LIMIT 可以限制掃描。
日期少一天或多一天
S3 day 與 time 是 UTC。台灣日期查詢需處理 Asia/Taipei +08:00。
同一時間有多個 log files
ALB 每個 node 約每五分鐘各自產生檔案,也可能同一區間產生多個檔。不要用檔案數當 request 數。
二十二、準確性限制
ALB access logs 是 best-effort,適合流量趨勢與 migration evidence,不是財務或計費級完整帳本。
另外要排除:
- 繞過 pro360-api ALB 的 direct/internal call。
- 其他 ALB。
- Webhook、cron、queue。
- 舊版 App 與季節性流量。
- URL normalization 誤合併。
- OPTIONS 和 business request 混算。
90 天為 0 只能標 LEGACY_DORMANT_CANDIDATE。
二十三、每次獨立操作 Checklist
開始前
- AWS Account/Region 正確。
- Workgroup 正確且有 scan limit。
- Database/table 正確。
- Source/result S3 權限正常。
- 統計日期已換成需要的 UTC range。
先跑一天
- total_requests > 0。
- elb filter 正確。
- request_path 正常。
- target_group/rule priority 有值。
- 記錄 Data scanned。
再跑 90 天
- 起點、中間、終點都有資料。
- 正規化抽查完成。
- OPTIONS 分開。
- 2xx/4xx/5xx 分開。
- last_seen 有值。
- target group/rule 已輸出。
匯出後
- CSV 存在非 git evidence 資料夾。
- 保存 Query execution ID。
- 保存 Data scanned 與 workgroup。
- 不保存 query string、IP 或 raw URL。
- 與 legacy/new code inventory 合併。
- 零流量項目仍標待確認。
二十四、主線執行順序
1. 管理員開 Athena/Glue/S3-result 權限
2. 確認 workgroup 與 scan limit
3. 找既有 database/table
4. 沒有 table 才由管理員建立 partition projection table
5. 跑一天 sanity query
6. 查看 Data scanned
7. 確認 90 天 retention
8. 跑單次 7/30/90 統計
9. 匯出 CSV
10. 合併 code inventory 與 LB rules
11. 產生 remaining API count
12. 安排 migration batches