ΠΠ±ΡΡΠ½ΠΎ Π΄Π»Ρ ΠΌΠΎΠ½ΠΈΡΠΎΡΠΈΠ½Π³Π° ΠΈ Π°Π½Π°Π»ΠΈΠ·Π° ΡΠ°Π±ΠΎΡΡ Nginx ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΡΡ ΠΊΠΎΠΌΠΌΠ΅ΡΡΠ΅ΡΠΊΠΈΠ΅ ΠΏΡΠΎΠ΄ΡΠΊΡΡ ΠΈΠ»ΠΈ Π³ΠΎΡΠΎΠ²ΡΠ΅ open-source Π°Π»ΡΡΠ΅ΡΠ½Π°ΡΠΈΠ²Ρ, ΡΠ°ΠΊΠΈΠ΅ ΠΊΠ°ΠΊ Prometheus + Grafana. ΠΡΠΎ Ρ ΠΎΡΠΎΡΠΈΠΉ Π²Π°ΡΠΈΠ°Π½Ρ Π΄Π»Ρ ΠΌΠΎΠ½ΠΈΡΠΎΡΠΈΠ½Π³Π° ΠΈΠ»ΠΈ real-time Π°Π½Π°Π»ΠΈΡΠΈΠΊΠΈ, Π½ΠΎ Π½Π΅ ΡΠ»ΠΈΡΠΊΠΎΠΌ ΡΠ΄ΠΎΠ±Π½ΡΠΉ Π΄Π»Ρ ΠΈΡΡΠΎΡΠΈΡΠ΅ΡΠΊΠΎΠ³ΠΎ Π°Π½Π°Π»ΠΈΠ·Π°. ΠΠ° Π»ΡΠ±ΠΎΠΌ ΠΏΠΎΠΏΡΠ»ΡΡΠ½ΠΎΠΌ ΡΠ΅ΡΡΡΡΠ΅ ΠΎΠ±ΡΠ΅ΠΌ Π΄Π°Π½Π½ΡΡ ΠΈΠ· Π»ΠΎΠ³ΠΎΠ² nginx Π±ΡΡΡΡΠΎ ΡΠ°ΡΡΠ΅Ρ, ΠΈ Π΄Π»Ρ Π°Π½Π°Π»ΠΈΠ·Π° Π±ΠΎΠ»ΡΡΠΎΠ³ΠΎ ΠΎΠ±ΡΠ΅ΠΌΠ° Π΄Π°Π½Π½ΡΡ Π»ΠΎΠ³ΠΈΡΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ΡΡΠΎ-ΡΠΎ Π±ΠΎΠ»Π΅Π΅ ΡΠΏΠ΅ΡΠΈΠ°Π»ΠΈΠ·ΠΈΡΠΎΠ²Π°Π½Π½ΠΎΠ΅.
Π ΡΡΠΎΠΉ ΡΡΠ°ΡΡΠ΅ Ρ ΡΠ°ΡΡΠΊΠ°ΠΆΡ, ΠΊΠ°ΠΊ ΠΌΠΎΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ
TL:DR;
ΠΠ»Ρ ΡΠ±ΠΎΡΠ° ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠΈ ΠΌΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌ
Π‘ΠΎΠ±ΠΈΡΠ°Π΅ΠΌ Π»ΠΎΠ³ΠΈ Nginx
ΠΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ, Π»ΠΎΠ³ΠΈ Nginx Π²ΡΠ³Π»ΡΠ΄ΡΡ ΠΊΠ°ΠΊ-ΡΠΎ ΡΠ°ΠΊ:
4/9/2019 12:58:17 PM1.1.1.1 - - [09/Apr/2019:09:58:17 +0000] "GET /sign-up HTTP/2.0" 200 9168 "https://example.com/sign-in" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36" "-"
4/9/2019 12:58:17 PM1.1.1.1 - - [09/Apr/2019:09:58:17 +0000] "GET /sign-in HTTP/2.0" 200 9168 "https://example.com/sign-up" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36" "-"
ΠΡ ΠΌΠΎΠΆΠ½ΠΎ ΡΠ°ΡΠΏΠ°ΡΡΠΈΡΡ, Π½ΠΎ Π³ΠΎΡΠ°Π·Π΄ΠΎ ΠΏΡΠΎΡΠ΅ ΠΏΠΎΠΏΡΠ°Π²ΠΈΡΡ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ Nginx, ΡΡΠΎΠ±Ρ ΠΎΠ½ Π²ΡΠ΄Π°Π²Π°Π» Π»ΠΎΠ³ΠΈ Π² JSON:
log_format json_combined escape=json '{ "created_at": "$msec", '
'"remote_addr": "$remote_addr", '
'"remote_user": "$remote_user", '
'"request": "$request", '
'"status": $status, '
'"bytes_sent": $bytes_sent, '
'"request_length": $request_length, '
'"request_time": $request_time, '
'"http_referrer": "$http_referer", '
'"http_x_forwarded_for": "$http_x_forwarded_for", '
'"http_user_agent": "$http_user_agent" }';
access_log /var/log/nginx/access.log json_combined;
S3 Π΄Π»Ρ Ρ ΡΠ°Π½Π΅Π½ΠΈΡ
Π§ΡΠΎΠ±Ρ Ρ ΡΠ°Π½ΠΈΡΡ Π»ΠΎΠ³ΠΈ, ΠΌΡ Π±ΡΠ΄Π΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ S3. ΠΡΠΎ ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ΅Ρ Ρ ΡΠ°Π½ΠΈΡΡ ΠΈ Π°Π½Π°Π»ΠΈΠ·ΠΈΡΠΎΠ²Π°ΡΡ Π»ΠΎΠ³ΠΈ Π² ΠΎΠ΄Π½ΠΎΠΌ ΠΌΠ΅ΡΡΠ΅, ΡΠ°ΠΊ ΠΊΠ°ΠΊ Athena ΠΌΠΎΠΆΠ΅Ρ ΡΠ°Π±ΠΎΡΠ°ΡΡ Ρ Π΄Π°Π½Π½ΡΠΌΠΈ Π² S3 Π½Π°ΠΏΡΡΠΌΡΡ. ΠΠ°Π»ΡΡΠ΅ Π² ΡΡΠ°ΡΡΠ΅ Ρ ΡΠ°ΡΡΠΊΠ°ΠΆΡ, ΠΊΠ°ΠΊ ΠΏΡΠ°Π²ΠΈΠ»ΡΠ½ΠΎ ΡΠΊΠ»Π°Π΄ΡΠ²Π°ΡΡ ΠΈ ΠΏΡΠΎΡΠ΅ΡΡΠΈΡΡ Π»ΠΎΠ³ΠΈ, Π½ΠΎ Π΄Π»Ρ Π½Π°ΡΠ°Π»Π° Π½Π°ΠΌ Π½ΡΠΆΠ΅Π½ ΡΠΈΡΡΡΠΉ Π±Π°ΠΊΠ΅Ρ Π² S3, Π² ΠΊΠΎΡΠΎΡΠΎΠΌ Π½ΠΈΡΠ΅Π³ΠΎ Π±ΠΎΠ»ΡΡΠ΅ Ρ ΡΠ°Π½ΠΈΡΡΡΡ Π½Π΅ Π±ΡΠ΄Π΅Ρ. Π‘ΡΠΎΠΈΡ Π·Π°ΡΠ°Π½Π΅Π΅ ΠΏΠΎΠ΄ΡΠΌΠ°ΡΡ, Π² ΠΊΠ°ΠΊΠΎΠΌ ΡΠ΅Π³ΠΈΠΎΠ½Π΅ Π²Ρ ΡΠΎΠ·Π΄Π°Π΄ΠΈΡΠ΅ Π±Π°ΠΊΠ΅Ρ, ΠΏΠΎΡΠΎΠΌΡ ΡΡΠΎ Athena Π΄ΠΎΡΡΡΠΏΠ½Π° Π½Π΅ Π²ΠΎ Π²ΡΠ΅Ρ ΡΠ΅Π³ΠΈΠΎΠ½Π°Ρ .
Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ ΡΡ Π΅ΠΌΡ Π² ΠΊΠΎΠ½ΡΠΎΠ»ΠΈ Athena
Π‘ΠΎΠ·Π΄Π°Π΄ΠΈΠΌ ΡΠ°Π±Π»ΠΈΡΡ Π² Athena Π΄Π»Ρ Π»ΠΎΠ³ΠΎΠ². ΠΠ½Π° Π½ΡΠΆΠ½Π° ΠΈ Π΄Π»Ρ Π·Π°ΠΏΠΈΡΠΈ, ΠΈ Π΄Π»Ρ ΡΡΠ΅Π½ΠΈΡ, Π΅ΡΠ»ΠΈ Π²Ρ ΠΏΠ»Π°Π½ΠΈΡΡΠ΅ΡΠ΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Kinesis Firehose. ΠΡΠΊΡΡΠ²Π°Π΅ΡΠ΅ ΠΊΠΎΠ½ΡΠΎΠ»Ρ Athena ΠΈ ΡΠΎΠ·Π΄Π°Π΅ΡΠ΅ ΡΠ°Π±Π»ΠΈΡΡ:
SQL ΡΠΎΠ·Π΄Π°Π½ΠΈΡ ΡΠ°Π±Π»ΠΈΡΡ
CREATE EXTERNAL TABLE `kinesis_logs_nginx`(
`created_at` double,
`remote_addr` string,
`remote_user` string,
`request` string,
`status` int,
`bytes_sent` int,
`request_length` int,
`request_time` double,
`http_referrer` string,
`http_x_forwarded_for` string,
`http_user_agent` string)
ROW FORMAT SERDE
'org.apache.hadoop.hive.ql.io.orc.OrcSerde'
STORED AS INPUTFORMAT
'org.apache.hadoop.hive.ql.io.orc.OrcInputFormat'
OUTPUTFORMAT
'org.apache.hadoop.hive.ql.io.orc.OrcOutputFormat'
LOCATION
's3://<YOUR-S3-BUCKET>'
TBLPROPERTIES ('has_encrypted_data'='false');
Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ Kinesis Firehose Stream
Kinesis Firehose Π·Π°ΠΏΠΈΡΠ΅Ρ Π΄Π°Π½Π½ΡΠ΅, ΠΏΠΎΠ»ΡΡΠ΅Π½Π½ΡΠ΅ ΠΎΡ Nginx, Π² S3 Π² Π²ΡΠ±ΡΠ°Π½Π½ΠΎΠΌ ΡΠΎΡΠΌΠ°ΡΠ΅, ΡΠ°Π·Π±ΠΈΠ² ΠΏΠΎ Π΄ΠΈΡΠ΅ΠΊΡΠΎΡΠΈΡΠΌ Π² ΡΠΎΡΠΌΠ°ΡΠ΅ ΠΠΠΠ/ΠΠ/ΠΠ/Π§Π§. ΠΡΠΎ ΠΏΡΠΈΠ³ΠΎΠ΄ΠΈΡΡΡ ΠΏΡΠΈ ΡΡΠ΅Π½ΠΈΠΈ Π΄Π°Π½Π½ΡΡ . ΠΠΎΠΆΠ½ΠΎ, ΠΊΠΎΠ½Π΅ΡΠ½ΠΎ, ΠΏΠΈΡΠ°ΡΡ Π½Π°ΠΏΡΡΠΌΡΡ Π² S3 ΠΈΠ· fluentd, Π½ΠΎ Π² ΡΡΠΎΠΌ ΡΠ»ΡΡΠ°Π΅ ΠΏΡΠΈΠ΄Π΅ΡΡΡ ΠΏΠΈΡΠ°ΡΡ JSON, Π° ΡΡΠΎ Π½Π΅ΡΡΡΠ΅ΠΊΡΠΈΠ²Π½ΠΎ ΠΈΠ·-Π·Π° Π±ΠΎΠ»ΡΡΠΎΠ³ΠΎ ΡΠ°Π·ΠΌΠ΅ΡΠ° ΡΠ°ΠΉΠ»ΠΎΠ². Π ΡΠΎΠΌΡ ΠΆΠ΅, ΠΏΡΠΈ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠΈ PrestoDB ΠΈΠ»ΠΈ Athena, JSON β ΡΠ°ΠΌΡΠΉ ΠΌΠ΅Π΄Π»Π΅Π½Π½ΡΠΉ ΡΠΎΡΠΌΠ°Ρ Π΄Π°Π½Π½ΡΡ . Π’Π°ΠΊ ΡΡΠΎ ΠΎΡΠΊΡΡΠ²Π°Π΅ΠΌ ΠΊΠΎΠ½ΡΠΎΠ»Ρ Kinesis Firehose, Π½Π°ΠΆΠΈΠΌΠ°Π΅ΠΌ «Create delivery stream», Π²ΡΠ±ΠΈΡΠ°Π΅ΠΌ «direct PUT» Π² ΠΏΠΎΠ»Π΅ «delivery»:
Π ΡΠ»Π΅Π΄ΡΡΡΠ΅ΠΉ Π²ΠΊΠ»Π°Π΄ΠΊΠ΅ Π²ΡΠ±ΠΈΡΠ°Π΅ΠΌ «Record format conversion» β «Enabled» ΠΈ Π²ΡΠ±ΠΈΡΠ°Π΅ΠΌ «Apache ORC» ΠΊΠ°ΠΊ ΡΠΎΡΠΌΠ°Ρ Π΄Π»Ρ Π·Π°ΠΏΠΈΡΠΈ. Π‘ΠΎΠ³Π»Π°ΡΠ½ΠΎ ΠΈΡΡΠ»Π΅Π΄ΠΎΠ²Π°Π½ΠΈΡΠΌ Π½Π΅ΠΊΠΎΡΠΎΡΠΎΠ³ΠΎ
ΠΡΠ±ΠΈΡΠ°Π΅ΠΌ S3 Π΄Π»Ρ Ρ ΡΠ°Π½Π΅Π½ΠΈΡ ΠΈ Π±Π°ΠΊΠ΅Ρ, ΠΊΠΎΡΠΎΡΡΠΉ ΠΌΡ ΡΠΎΠ·Π΄Π°Π»ΠΈ ΡΠ°Π½ΡΡΠ΅. Aws Glue Crawler, ΠΏΡΠΎ ΠΊΠΎΡΠΎΡΡΠΉ Ρ ΡΠ°ΡΡΠΊΠ°ΠΆΡ ΡΡΡΡ ΠΏΠΎΠ·ΠΆΠ΅, Π½Π΅ ΡΠΌΠ΅Π΅Ρ ΡΠ°Π±ΠΎΡΠ°ΡΡ Ρ ΠΏΡΠ΅ΡΠΈΠΊΡΠ°ΠΌΠΈ Π² S3 Π±Π°ΠΊΠ΅ΡΠ΅, ΡΠ°ΠΊ ΡΡΠΎ Π΅Π³ΠΎ Π²Π°ΠΆΠ½ΠΎ ΠΎΡΡΠ°Π²ΠΈΡΡ ΠΏΡΡΡΡΠΌ.
ΠΡΡΠ°Π»ΡΠ½ΡΠ΅ ΠΎΠΏΡΠΈΠΈ ΠΌΠΎΠΆΠ½ΠΎ ΠΈΠ·ΠΌΠ΅Π½ΡΡΡ Π² Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠΈ ΠΎΡ Π²Π°ΡΠ΅ΠΉ Π½Π°Π³ΡΡΠ·ΠΊΠΈ, Ρ ΠΎΠ±ΡΡΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΡ Π΄Π΅ΡΠΎΠ»ΡΠ½ΡΠ΅. ΠΠ±ΡΠ°ΡΠΈΡΠ΅ Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅, ΡΡΠΎ ΡΠΆΠ°ΡΠΈΠ΅ S3 Π½Π΅Π΄ΠΎΡΡΡΠΏΠ½ΠΎ, Π½ΠΎ ORC ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅Ρ ΡΠΎΠ±ΡΡΠ²Π΅Π½Π½ΠΎΠ΅ ΡΠΆΠ°ΡΠΈΠ΅ ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ.
Fluentd
Π’Π΅ΠΏΠ΅ΡΡ, ΠΊΠΎΠ³Π΄Π° Ρ Π½Π°Ρ Π½Π°ΡΡΡΠΎΠ΅Π½ΠΎ Ρ
ΡΠ°Π½Π΅Π½ΠΈΠ΅ ΠΈ ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ Π»ΠΎΠ³ΠΎΠ², Π½Π°Π΄ΠΎ Π½Π°ΡΡΡΠΎΠΈΡΡ ΠΎΡΠΏΡΠ°Π²ΠΊΡ. ΠΡ Π±ΡΠ΄Π΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ
ΠΠ»Ρ Π½Π°ΡΠ°Π»Π°, Π½Π°ΠΌ Π½ΡΠΆΠ΅Π½ ΡΠ°ΠΉΠ» ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ fluent.conf. Π‘ΠΎΠ·Π΄Π°ΠΉΡΠ΅ Π΅Π³ΠΎ ΠΈ Π΄ΠΎΠ±Π°Π²ΡΡΠ΅ source:
port 24224
bind 0.0.0.0
Π’Π΅ΠΏΠ΅ΡΡ ΠΌΠΎΠΆΠ½ΠΎ Π·Π°ΠΏΡΡΡΠΈΡΡ Fluentd ΡΠ΅ΡΠ²Π΅Ρ. ΠΡΠ»ΠΈ Π²Π°ΠΌ Π½ΡΠΆΠ½Π° Π±ΠΎΠ»Π΅Π΅ ΠΏΡΠΎΠ΄Π²ΠΈΠ½ΡΡΠ°Ρ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ, Π½Π°
$ docker run
-d
-p 24224:24224
-p 24224:24224/udp
-v /data:/fluentd/log
-v <PATH-TO-FLUENT-CONF>:/fluentd/etc fluentd
-c /fluentd/etc/fluent.conf
fluent/fluentd:stable
ΠΡΠ° ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅Ρ ΠΏΡΡΡ /fluentd/log
Π΄Π»Ρ ΠΊΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΡ Π»ΠΎΠ³ΠΎΠ² ΠΏΠ΅ΡΠ΅Π΄ ΠΎΡΠΏΡΠ°Π²ΠΊΠΎΠΉ. ΠΠΎΠΆΠ½ΠΎ ΠΎΠ±ΠΎΠΉΡΠΈΡΡ Π±Π΅Π· ΡΡΠΎΠ³ΠΎ, Π½ΠΎ ΡΠΎΠ³Π΄Π° ΠΏΡΠΈ ΠΏΠ΅ΡΠ΅Π·Π°ΠΏΡΡΠΊΠ΅ ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΡΠ΅ΡΡΡΡ Π²ΡΠ΅ Π·Π°ΠΊΡΡΠΈΡΠΎΠ²Π°Π½Π½ΠΎΠ΅ Π½Π΅ΠΏΠΎΡΠΈΠ»ΡΠ½ΡΠΌ ΡΡΡΠ΄ΠΎΠΌ. ΠΠΎΡΡ ΡΠΎΠΆΠ΅ ΠΌΠΎΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Π»ΡΠ±ΠΎΠΉ, 24224 β ΡΡΠΎ Π΄Π΅ΡΠΎΠ»ΡΠ½ΡΠΉ ΠΏΠΎΡΡ Fluentd.
Π’Π΅ΠΏΠ΅ΡΡ, ΠΊΠΎΠ³Π΄Π° Ρ Π½Π°Ρ Π΅ΡΡΡ Π·Π°ΠΏΡΡΠ΅Π½Π½ΡΠΉ Fluentd, ΠΌΡ ΠΌΠΎΠΆΠ΅ΠΌ ΠΎΡΠΏΡΠ°Π²ΠΈΡΡ ΡΡΠ΄Π° Π»ΠΎΠ³ΠΈ Nginx. ΠΡ ΠΎΠ±ΡΡΠ½ΠΎ Π·Π°ΠΏΡΡΠΊΠ°Π΅ΠΌ Nginx Π² Docker-ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅ΡΠ΅, ΠΈ Π² ΡΡΠΎΠΌ ΡΠ»ΡΡΠ°Π΅ Ρ Docker Π΅ΡΡΡ Π½Π°ΡΠΈΠ²Π½ΡΠΉ Π΄ΡΠ°ΠΉΠ²Π΅Ρ Π»ΠΎΠ³ΠΎΠ² Π΄Π»Ρ Fluentd:
$ docker run
--log-driver=fluentd
--log-opt fluentd-address=<FLUENTD-SERVER-ADDRESS>
--log-opt tag="{{.Name}}"
-v /some/content:/usr/share/nginx/html:ro
-d
nginx
ΠΡΠ»ΠΈ Π²Ρ Π·Π°ΠΏΡΡΠΊΠ°Π΅ΡΠ΅ Nginx ΠΈΠ½Π°ΡΠ΅, Π²Ρ ΠΌΠΎΠΆΠ΅ΡΠ΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Π»ΠΎΠ³-ΡΠ°ΠΉΠ»Ρ, Π² Fluentd Π΅ΡΡΡ
ΠΠΎΠ±Π°Π²ΠΈΠΌ Π² ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ Fluent ΠΏΠ°ΡΡΠΈΠ½Π³ Π»ΠΎΠ³ΠΎΠ², Π½Π°ΡΡΡΠΎΠ΅Π½Π½ΡΠΉ Π²ΡΡΠ΅:
<filter YOUR-NGINX-TAG.*>
@type parser
key_name log
emit_invalid_record_to_error false
<parse>
@type json
</parse>
</filter>
Π ΠΎΡΠΏΡΠ°Π²ΠΊΡ Π»ΠΎΠ³ΠΎΠ² Π² Kinesis, ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΡ
<match YOUR-NGINX-TAG.*>
@type kinesis_firehose
region region
delivery_stream_name <YOUR-KINESIS-STREAM-NAME>
aws_key_id <YOUR-AWS-KEY-ID>
aws_sec_key <YOUR_AWS-SEC_KEY>
</match>
Athena
ΠΡΠ»ΠΈ Π²Ρ Π²ΡΠ΅ ΠΏΡΠ°Π²ΠΈΠ»ΡΠ½ΠΎ Π½Π°ΡΡΡΠΎΠΈΠ»ΠΈ, ΡΠΎ ΡΠ΅ΡΠ΅Π· Π½Π΅ΠΊΠΎΡΠΎΡΠΎΠ΅ Π²ΡΠ΅ΠΌΡ (ΠΏΠΎ-ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ Kinesis Π·Π°ΠΏΠΈΡΡΠ²Π°Π΅Ρ ΠΏΠΎΠ»ΡΡΠ΅Π½Π½ΡΠ΅ Π΄Π°Π½Π½ΡΠ΅ ΡΠ°Π· Π² 10 ΠΌΠΈΠ½ΡΡ) Π²Ρ Π΄ΠΎΠ»ΠΆΠ½Ρ ΡΠ²ΠΈΠ΄Π΅ΡΡ ΡΠ°ΠΉΠ»Ρ Π»ΠΎΠ³ΠΎΠ² Π² S3. Π ΠΌΠ΅Π½Ρ «monitoring» Kinesis Firehose ΠΌΠΎΠΆΠ½ΠΎ ΡΠ²ΠΈΠ΄Π΅ΡΡ, ΡΠΊΠΎΠ»ΡΠΊΠΎ Π΄Π°Π½Π½ΡΡ Π·Π°ΠΏΠΈΡΠ°Π½ΠΎ Π² S3, Π° ΡΠ°ΠΊ ΠΆΠ΅ ΠΎΡΠΈΠ±ΠΊΠΈ. ΠΠ΅ Π·Π°Π±ΡΠ΄ΡΡΠ΅ Π΄Π°ΡΡ Π΄ΠΎΡΡΡΠΏ Π½Π° Π·Π°ΠΏΠΈΡΡ Π² Π±Π°ΠΊΠ΅Ρ S3 Π΄Π»Ρ ΡΠΎΠ»ΠΈ Kinesis. ΠΡΠ»ΠΈ Kinesis ΡΡΠΎ-ΡΠΎ Π½Π΅ ΡΠΌΠΎΠ³ ΡΠ°ΡΠΏΠ°ΡΡΠΈΡΡ, ΠΎΠ½ ΡΠ»ΠΎΠΆΠΈΡ ΠΎΡΠΈΠ±ΠΊΠΈ Π² ΡΠΎΠΌ ΠΆΠ΅ Π±Π°ΠΊΠ΅ΡΠ΅.
Π’Π΅ΠΏΠ΅ΡΡ ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΡΠΌΠΎΡΡΠ΅ΡΡ Π΄Π°Π½Π½ΡΠ΅ Π² Athena. ΠΠ°Π²Π°ΠΉΡΠ΅ Π½Π°ΠΉΠ΄Π΅ΠΌ ΡΠ²Π΅ΠΆΠΈΠ΅ Π·Π°ΠΏΡΠΎΡΡ, Π½Π° ΠΊΠΎΡΠΎΡΡΠ΅ ΠΌΡ ΠΎΡΠ΄Π°Π»ΠΈ ΠΎΡΠΈΠ±ΠΊΠΈ:
SELECT * FROM "db_name"."table_name" WHERE status > 499 ORDER BY created_at DESC limit 10;
Π‘ΠΊΠ°Π½ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ Π²ΡΠ΅Ρ Π·Π°ΠΏΠΈΡΠ΅ΠΉ Π½Π° ΠΊΠ°ΠΆΠ΄ΡΠΉ Π·Π°ΠΏΡΠΎΡ
Π’Π΅ΠΏΠ΅ΡΡ Π½Π°ΡΠΈ Π»ΠΎΠ³ΠΈ ΠΎΠ±ΡΠ°Π±ΠΎΡΠ°Π½Ρ ΠΈ ΡΠ»ΠΎΠΆΠ΅Π½Ρ Π² S3 Π² ORC, ΡΠΆΠ°ΡΡ ΠΈ Π³ΠΎΡΠΎΠ²Ρ ΠΊ Π°Π½Π°Π»ΠΈΠ·Ρ. Kinesis Firehose Π΄Π°ΠΆΠ΅ ΡΠ°Π·Π»ΠΎΠΆΠΈΠ» ΠΈΡ ΠΏΠΎ Π΄ΠΈΡΠ΅ΠΊΡΠΎΡΠΈΡΠΌ Π½Π° ΠΊΠ°ΠΆΠ΄ΡΠΉ ΡΠ°Ρ. ΠΠ΄Π½Π°ΠΊΠΎ, ΠΏΠΎΠΊΠ° ΡΠ°Π±Π»ΠΈΡΠ° Π½Π΅ ΠΏΠ°ΡΡΠΈΡΠΈΡΠΎΠ²Π°Π½Π°, Athena Π±ΡΠ΄Π΅Ρ Π·Π°Π³ΡΡΠΆΠ°ΡΡ Π΄Π°Π½Π½ΡΠ΅ Π·Π° Π²ΡΠ΅ Π²ΡΠ΅ΠΌΡ Π½Π° ΠΊΠ°ΠΆΠ΄ΡΠΉ Π·Π°ΠΏΡΠΎΡ, Π·Π° ΡΠ΅Π΄ΠΊΠΈΠΌ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅ΠΌ. ΠΡΠΎ Π±ΠΎΠ»ΡΡΠ°Ρ ΠΏΡΠΎΠ±Π»Π΅ΠΌΠ° ΠΏΠΎ Π΄Π²ΡΠΌ ΠΏΡΠΈΡΠΈΠ½Π°ΠΌ:
- ΠΠ±ΡΠ΅ΠΌ Π΄Π°Π½Π½ΡΡ ΠΏΠΎΡΡΠΎΡΠ½Π½ΠΎ ΡΠ°ΡΡΠ΅Ρ, Π·Π°ΠΌΠ΅Π΄Π»ΡΡ Π·Π°ΠΏΡΠΎΡΡ;
- Π‘ΡΠ΅Ρ Π·Π° Athena Π²ΡΡΡΠ°Π²Π»ΡΠ΅ΡΡΡ Π² Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠΈ ΠΎΡ ΠΎΠ±ΡΠ΅ΠΌΠ° ΠΏΡΠΎΡΠΊΠ°Π½ΠΈΡΠΎΠ²Π°Π½Π½ΡΡ Π΄Π°Π½Π½ΡΡ , Ρ ΠΌΠΈΠ½ΠΈΠΌΡΠΌΠΎΠΌ 10 ΠΠ Π·Π° ΠΊΠ°ΠΆΠ΄ΡΠΉ Π·Π°ΠΏΡΠΎΡ.
Π§ΡΠΎΠ±Ρ ΠΈΡΠΏΡΠ°Π²ΠΈΡΡ ΡΡΠΎ, ΠΌΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌ AWS Glue Crawler, ΠΊΠΎΡΠΎΡΡΠΉ ΠΏΡΠΎΡΠΊΠ°Π½ΠΈΡΡΠ΅Ρ Π΄Π°Π½Π½ΡΠ΅ Π² S3 ΠΈ Π·Π°ΠΏΠΈΡΠ΅Ρ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ ΠΎ ΠΏΠ°ΡΡΠΈΡΠΈΡΡ Π² Glue Metastore. ΠΡΠΎ ΠΏΠΎΠ·Π²ΠΎΠ»ΠΈΡ Π½Π°ΠΌ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ΠΏΠ°ΡΡΠΈΡΠΈΠΈ ΠΊΠ°ΠΊ ΡΠΈΠ»ΡΡΡ ΠΏΡΠΈ Π·Π°ΠΏΡΠΎΡΠ°Ρ Π² Athena, ΠΈ ΠΎΠ½Π° Π±ΡΠ΄Π΅Ρ ΡΠΊΠ°Π½ΠΈΡΠΎΠ²Π°ΡΡ ΡΠΎΠ»ΡΠΊΠΎ Π΄ΠΈΡΠ΅ΠΊΡΠΎΡΠΈΠΈ, ΡΠΊΠ°Π·Π°Π½Π½ΡΠ΅ Π² Π·Π°ΠΏΡΠΎΡΠ΅.
ΠΠ°ΡΡΡΠ°ΠΈΠ²Π°Π΅ΠΌ Amazon Glue Crawler
Amazon Glue Crawler ΡΠΊΠ°Π½ΠΈΡΡΠ΅Ρ Π²ΡΠ΅ Π΄Π°Π½Π½ΡΠ΅ Π² S3 Π±Π°ΠΊΠ΅ΡΠ΅ ΠΈ ΡΠΎΠ·Π΄Π°Π΅Ρ ΡΠ°Π±Π»ΠΈΡΡ Ρ ΠΏΠ°ΡΡΠΈΡΠΈΡΠΌΠΈ. Π‘ΠΎΠ·Π΄Π°ΠΉΡΠ΅ Glue Crawler ΠΈΠ· ΠΊΠΎΠ½ΡΠΎΠ»ΠΈ AWS Glue ΠΈ Π΄ΠΎΠ±Π°Π²ΡΡΠ΅ Π±Π°ΠΊΠ΅Ρ, Π² ΠΊΠΎΡΠΎΡΠΎΠΌ Π²Ρ Ρ ΡΠ°Π½ΠΈΡΠ΅ Π΄Π°Π½Π½ΡΠ΅. ΠΡ ΠΌΠΎΠΆΠ΅ΡΠ΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ΠΎΠ΄ΠΈΠ½ ΠΊΡΠ°ΡΠ»Π΅Ρ Π΄Π»Ρ Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΈΡ Π±Π°ΠΊΠ΅ΡΠΎΠ², Π² ΡΡΠΎΠΌ ΡΠ»ΡΡΠ°Π΅ ΠΎΠ½ ΡΠΎΠ·Π΄Π°ΡΡ ΡΠ°Π±Π»ΠΈΡΡ Π² ΡΠΊΠ°Π·Π°Π½Π½ΠΎΠΉ Π±Π°Π·Π΅ Π΄Π°Π½Π½ΡΡ Ρ Π½Π°Π·Π²Π°Π½ΠΈΡΠΌΠΈ, ΡΠΎΠ²ΠΏΠ°Π΄Π°ΡΡΠΈΠΌΠΈ Ρ Π½Π°Π·Π²Π°Π½ΠΈΡΠΌΠΈ Π±Π°ΠΊΠ΅ΡΠΎΠ². ΠΡΠ»ΠΈ Π²Ρ ΠΏΠ»Π°Π½ΠΈΡΡΠ΅ΡΠ΅ ΠΏΠΎΡΡΠΎΡΠ½Π½ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ΡΡΠΈ Π΄Π°Π½Π½ΡΠ΅, Π½Π΅ Π·Π°Π±ΡΠ΄ΡΡΠ΅ Π½Π°ΡΡΡΠΎΠΈΡΡ ΡΠ°ΡΠΏΠΈΡΠ°Π½ΠΈΠ΅ Π·Π°ΠΏΡΡΠΊΠ° Crawler Π² ΡΠΎΠΎΡΠ²Π΅ΡΡΡΠ²ΠΈΠΈ Ρ Π²Π°ΡΠΈΠΌΠΈ ΠΏΠΎΡΡΠ΅Π±Π½ΠΎΡΡΡΠΌΠΈ. ΠΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌ ΠΎΠ΄ΠΈΠ½ Crawler Π΄Π»Ρ Π²ΡΠ΅Ρ ΡΠ°Π±Π»ΠΈΡ, ΠΊΠΎΡΠΎΡΡΠΉ Π·Π°ΠΏΡΡΠΊΠ°Π΅ΡΡΡ ΠΊΠ°ΠΆΠ΄ΡΠΉ ΡΠ°Ρ.
ΠΠ°ΡΡΠΈΡΠΈΡΠΎΠ²Π°Π½Π½ΡΠ΅ ΡΠ°Π±Π»ΠΈΡΡ
ΠΠΎΡΠ»Π΅ ΠΏΠ΅ΡΠ²ΠΎΠ³ΠΎ Π·Π°ΠΏΡΡΠΊΠ° ΠΊΡΠ°ΡΠ»Π΅ΡΠ° Π² Π±Π°Π·Π΅ Π΄Π°Π½Π½ΡΡ , ΡΠΊΠ°Π·Π°Π½Π½ΠΎΠΉ Π² Π½Π°ΡΡΡΠΎΠΉΠΊΠ°Ρ , Π΄ΠΎΠ»ΠΆΠ½Ρ ΠΏΠΎΡΠ²ΠΈΡΡΡΡ ΡΠ°Π±Π»ΠΈΡΡ Π΄Π»Ρ ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΠΏΡΠΎΡΠΊΠ°Π½ΠΈΡΠΎΠ²Π°Π½Π½ΠΎΠ³ΠΎ Π±Π°ΠΊΠ΅ΡΠ°. ΠΡΠΊΡΠΎΠΉΡΠ΅ ΠΊΠΎΠ½ΡΠΎΠ»Ρ Athena ΠΈ Π½Π°ΠΉΠ΄ΠΈΡΠ΅ ΡΠ°Π±Π»ΠΈΡΡ Ρ Π»ΠΎΠ³Π°ΠΌΠΈ Nginx. ΠΠ°Π²Π°ΠΉΡΠ΅ ΠΏΠΎΠΏΡΠΎΠ±ΡΠ΅ΠΌ ΡΡΠΎ-Π½ΠΈΠ±ΡΠ΄Ρ ΠΏΡΠΎΡΠΈΡΠ°ΡΡ:
SELECT * FROM "default"."part_demo_kinesis_bucket"
WHERE(
partition_0 = '2019' AND
partition_1 = '04' AND
partition_2 = '08' AND
partition_3 = '06'
);
ΠΡΠΎΡ Π·Π°ΠΏΡΠΎΡ Π²ΡΠ±Π΅ΡΠ΅Ρ Π²ΡΠ΅ Π·Π°ΠΏΠΈΡΠΈ, ΠΏΠΎΠ»ΡΡΠ΅Π½Π½ΡΠ΅ Ρ 6 Π΄ΠΎ 7 ΡΡΡΠ° 8 Π°ΠΏΡΠ΅Π»Ρ 2019 Π³ΠΎΠ΄Π°. ΠΠΎ Π½Π°ΡΠΊΠΎΠ»ΡΠΊΠΎ ΡΡΠΎ ΡΡΡΠ΅ΠΊΡΠΈΠ²Π½Π΅Π΅, ΡΠ΅ΠΌ ΠΏΡΠΎΡΡΠΎ ΡΠΈΡΠ°ΡΡ ΠΈΠ· Π½Π΅-ΠΏΠ°ΡΡΠΈΡΠΈΡΠΎΠ²Π°Π½Π½ΠΎΠΉ ΡΠ°Π±Π»ΠΈΡΡ? ΠΠ°Π²Π°ΠΉΡΠ΅ ΡΠ·Π½Π°Π΅ΠΌ ΠΈ Π²ΡΠ±Π΅ΡΠ΅ΠΌ ΡΠ΅ ΠΆΠ΅ Π·Π°ΠΏΠΈΡΠΈ, ΠΎΡΡΠΈΠ»ΡΡΡΠΎΠ²Π°Π² ΠΈΡ ΠΏΠΎ ΡΠ°ΠΉΠΌΡΡΠ΅ΠΌΠΏΡ:
3.59 ΡΠ΅ΠΊΡΠ½Π΄Ρ ΠΈ 244.34 ΠΌΠ΅Π³Π°Π±Π°ΠΉΡ Π΄Π°Π½Π½ΡΡ Π½Π° Π΄Π°ΡΠ°ΡΠ΅ΡΠ΅, Π² ΠΊΠΎΡΠΎΡΠΎΠΌ Π²ΡΠ΅Π³ΠΎ Π½Π΅Π΄Π΅Π»Ρ Π»ΠΎΠ³ΠΎΠ². ΠΠΎΠΏΡΠΎΠ±ΡΠ΅ΠΌ ΡΠΈΠ»ΡΡΡ ΠΏΠΎ ΠΏΠ°ΡΡΠΈΡΠΈΡΠΌ:
Π§ΡΡΡ Π±ΡΡΡΡΠ΅Π΅, Π½ΠΎ ΡΠ°ΠΌΠΎΠ΅ Π²Π°ΠΆΠ½ΠΎΠ΅ β Π²ΡΠ΅Π³ΠΎ 1.23 ΠΌΠ΅Π³Π°Π±Π°ΠΉΡΠ° Π΄Π°Π½Π½ΡΡ ! ΠΡΠΎ Π±ΡΠ»ΠΎ Π±Ρ Π³ΠΎΡΠ°Π·Π΄ΠΎ Π΄Π΅ΡΠ΅Π²Π»Π΅, Π΅ΡΠ»ΠΈ Π±Ρ Π½Π΅ ΠΌΠΈΠ½ΠΈΠΌΠ°Π»ΡΠ½ΡΠ΅ 10 ΠΌΠ΅Π³Π°Π±Π°ΠΉΡ Π·Π° Π·Π°ΠΏΡΠΎΡ Π² ΠΏΡΠ°ΠΉΡΠΈΠ½Π³Π΅. ΠΠΎ Π²ΡΠ΅ ΡΠ°Π²Π½ΠΎ Π³ΠΎΡΠ°Π·Π΄ΠΎ Π»ΡΡΡΠ΅, Π° Π½Π° Π±ΠΎΠ»ΡΡΠΈΡ Π΄Π°ΡΠ°ΡΠ΅ΡΠ°Ρ ΡΠ°Π·Π½ΠΈΡΠ° Π±ΡΠ΄Π΅Ρ ΠΊΡΠ΄Π° Π±ΠΎΠ»Π΅Π΅ Π²ΠΏΠ΅ΡΠ°ΡΠ»ΡΡΡΠ΅ΠΉ.
Π‘ΠΎΠ±ΠΈΡΠ°Π΅ΠΌ Π΄ΡΡΠ±ΠΎΡΠ΄ Ρ ΠΏΠΎΠΌΠΎΡΡΡ Cube.js
Π§ΡΠΎΠ±Ρ ΡΠΎΠ±ΡΠ°ΡΡ Π΄ΡΡΠ±ΠΎΡΠ΄, ΠΌΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌ Π°Π½Π°Π»ΠΈΡΠΈΡΠ΅ΡΠΊΠΈΠΉ ΡΡΠ΅ΠΉΠΌΠ²ΠΎΡΠΊ Cube.js. Π£ Π½Π΅Π³ΠΎ Π΄ΠΎΠ²ΠΎΠ»ΡΠ½ΠΎ ΠΌΠ½ΠΎΠ³ΠΎ ΡΡΠ½ΠΊΡΠΈΠΉ, Π½ΠΎ Π½Π°Ρ ΠΈΠ½ΡΠ΅ΡΠ΅ΡΡΡΡ Π΄Π²Π΅: Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΡ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ΡΠΈΠ»ΡΡΡΡ ΠΏΠΎ ΠΏΠ°ΡΡΠΈΡΠΈΡΠΌ ΠΈ ΠΏΡΠ΅-Π°Π³ΡΠ΅Π³Π°ΡΠΈΠΈ Π΄Π°Π½Π½ΡΡ
. ΠΠ½ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅Ρ ΡΡ
Π΅ΠΌΡ Π΄Π°Π½Π½ΡΡ
Π‘ΠΎΠ·Π΄Π°Π΄ΠΈΠΌ Π½ΠΎΠ²ΠΎΠ΅ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Cube.js. Π’Π°ΠΊ ΠΊΠ°ΠΊ ΠΌΡ ΡΠΆΠ΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌ AWS-ΡΡΡΠΊ, Π»ΠΎΠ³ΠΈΡΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Lambda Π΄Π»Ρ Π΄Π΅ΠΏΠ»ΠΎΡ. ΠΡ ΠΌΠΎΠΆΠ΅ΡΠ΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ express-ΡΠ°Π±Π»ΠΎΠ½ Π΄Π»Ρ Π³Π΅Π½Π΅ΡΠ°ΡΠΈΠΈ, Π΅ΡΠ»ΠΈ ΠΏΠ»Π°Π½ΠΈΡΡΠ΅ΡΠ΅ Ρ
ΠΎΡΡΠΈΡΡ Cube.js Π±ΡΠΊΠ΅Π½Π΄ Π² Heroku ΠΈΠ»ΠΈ Docker. Π Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΠΈ ΠΎΠΏΠΈΡΠ°Π½Ρ Π΄ΡΡΠ³ΠΈΠ΅
$ npm install -g cubejs-cli
$ cubejs create nginx-log-analytics -t serverless -d athena
ΠΠ»Ρ Π½Π°ΡΡΡΠΎΠΉΠΊΠΈ Π΄ΠΎΡΡΡΠΏΠ° ΠΊ Π±Π°Π·Π΅ Π΄Π°Π½Π½ΡΡ
Π² cube.js ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΡΡΡΡ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠ΅ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ. ΠΠ΅Π½Π΅ΡΠ°ΡΠΎΡ ΡΠΎΠ·Π΄Π°ΡΡ ΡΠ°ΠΉΠ» .env, Π² ΠΊΠΎΡΠΎΡΠΎΠΌ Π²Ρ ΠΌΠΎΠΆΠ΅ΡΠ΅ ΡΠΊΠ°Π·Π°ΡΡ Π²Π°ΡΠΈ ΠΊΠ»ΡΡΠΈ Π΄Π»Ρ
Π’Π΅ΠΏΠ΅ΡΡ Π½Π°ΠΌ ΠΏΠΎΡΡΠ΅Π±ΡΠ΅ΡΡΡ
Π Π΄ΠΈΡΠ΅ΠΊΡΠΎΡΠΈΠΈ schema
, ΡΠΎΠ·Π΄Π°ΠΉΡΠ΅ ΡΠ°ΠΉΠ» Logs.js
. ΠΠΎΡ ΠΏΡΠΈΠΌΠ΅Ρ ΠΌΠΎΠ΄Π΅Π»ΠΈ Π΄Π°Π½Π½ΡΡ
Π΄Π»Ρ nginx:
ΠΠΎΠ΄ ΠΌΠΎΠ΄Π΅Π»ΠΈ
const partitionFilter = (from, to) => `
date(from_iso8601_timestamp(${from})) <= date_parse(partition_0 || partition_1 || partition_2, '%Y%m%d') AND
date(from_iso8601_timestamp(${to})) >= date_parse(partition_0 || partition_1 || partition_2, '%Y%m%d')
`
cube(`Logs`, {
sql: `
select * from part_demo_kinesis_bucket
WHERE ${FILTER_PARAMS.Logs.createdAt.filter(partitionFilter)}
`,
measures: {
count: {
type: `count`,
},
errorCount: {
type: `count`,
filters: [
{ sql: `${CUBE.isError} = 'Yes'` }
]
},
errorRate: {
type: `number`,
sql: `100.0 * ${errorCount} / ${count}`,
format: `percent`
}
},
dimensions: {
status: {
sql: `status`,
type: `number`
},
isError: {
type: `string`,
case: {
when: [{
sql: `${CUBE}.status >= 400`, label: `Yes`
}],
else: { label: `No` }
}
},
createdAt: {
sql: `from_unixtime(created_at)`,
type: `time`
}
}
});
ΠΠ΄Π΅ΡΡ ΠΌΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ
ΠΡ ΡΠ°ΠΊΠΆΠ΅ Π·Π°Π΄Π°Π΅ΠΌ ΠΌΠ΅ΡΡΠΈΠΊΠΈ ΠΈ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡ, ΠΊΠΎΡΠΎΡΡΠ΅ Ρ ΠΎΡΠΈΠΌ ΠΎΡΠΎΠ±ΡΠ°Π·ΠΈΡΡ Π½Π° Π΄ΡΡΠ±ΠΎΡΠ΄Π΅, ΠΈ ΡΠΊΠ°Π·ΡΠ²Π°Π΅ΠΌ ΠΏΡΠ΅-Π°Π³ΡΠ΅Π³Π°ΡΠΈΠΈ. Cube.js ΡΠΎΠ·Π΄Π°ΡΡ Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡΠ΅Π»ΡΠ½ΡΠ΅ ΡΠ°Π±Π»ΠΈΡΡ Ρ ΠΏΡΠ΅-Π°Π³ΡΠ΅Π³ΠΈΡΠΎΠ²Π°Π½Π½ΡΠΌΠΈ Π΄Π°Π½Π½ΡΠΌΠΈ ΠΈ Π±ΡΠ΄Π΅Ρ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ ΠΎΠ±Π½ΠΎΠ²Π»ΡΡΡ Π΄Π°Π½Π½ΡΠ΅ ΠΏΠΎ ΠΌΠ΅ΡΠ΅ ΠΏΠΎΡΡΡΠΏΠ»Π΅Π½ΠΈΡ. ΠΡΠΎ ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ΅Ρ Π½Π΅ ΡΠΎΠ»ΡΠΊΠΎ ΡΡΠΊΠΎΡΠΈΡΡ Π·Π°ΠΏΡΠΎΡΡ, Π½ΠΎ ΠΈ ΡΠ½ΠΈΠ·ΠΈΡΡ ΡΡΠΎΠΈΠΌΠΎΡΡΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ Athena.
ΠΠΎΠ±Π°Π²ΠΈΠΌ ΡΡΡ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ Π² ΡΠ°ΠΉΠ» ΡΡ Π΅ΠΌΡ Π΄Π°Π½Π½ΡΡ :
preAggregations: {
main: {
type: `rollup`,
measureReferences: [count, errorCount],
dimensionReferences: [isError, status],
timeDimensionReference: createdAt,
granularity: `day`,
partitionGranularity: `month`,
refreshKey: {
sql: FILTER_PARAMS.Logs.createdAt.filter((from, to) =>
`select
CASE WHEN from_iso8601_timestamp(${to}) + interval '3' day > now()
THEN date_trunc('hour', now()) END`
)
}
}
}
ΠΡ ΡΠΊΠ°Π·ΡΠ²Π°Π΅ΠΌ Π² ΡΡΠΎΠΉ ΠΌΠΎΠ΄Π΅Π»ΠΈ, ΡΡΠΎ Π½Π΅ΠΎΠ±Ρ
ΠΎΠ΄ΠΈΠΌΠΎ ΠΏΡΠ΅-Π°Π³ΡΠ΅Π³ΠΈΡΠΎΠ²Π°ΡΡ Π΄Π°Π½Π½ΡΠ΅ Π΄Π»Ρ Π²ΡΠ΅Ρ
ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌΡΡ
ΠΌΠ΅ΡΡΠΈΠΊ, ΠΈ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ΠΏΠ°ΡΡΠΈΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ ΠΏΠΎ ΠΌΠ΅ΡΡΡΠ°ΠΌ.
Π’Π΅ΠΏΠ΅ΡΡ ΠΌΡ ΠΌΠΎΠΆΠ΅ΠΌ ΡΠΎΠ±ΡΠ°ΡΡ Π΄ΡΡΠ±ΠΎΡΠ΄!
ΠΡΠΊΠ΅Π½Π΄ Cube.js ΠΏΡΠ΅Π΄ΠΎΡΡΠ°Π²Π»ΡΠ΅Ρ
Π‘Π΅ΡΠ²Π΅Ρ Cube.js ΠΏΡΠΈΠ½ΠΈΠΌΠ°Π΅Ρ Π·Π°ΠΏΡΠΎΡ Π²
{
"measures": ["Logs.errorCount"],
"timeDimensions": [
{
"dimension": "Logs.createdAt",
"dateRange": ["2019-01-01", "2019-01-07"],
"granularity": "day"
}
]
}
Π£ΡΡΠ°Π½ΠΎΠ²ΠΈΠΌ Cube.js ΠΊΠ»ΠΈΠ΅Π½Ρ ΠΈ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΡ React-ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Ρ ΡΠ΅ΡΠ΅Π· NPM:
$ npm i --save @cubejs-client/core @cubejs-client/react
ΠΠΌΠΏΠΎΡΡΠΈΠΌ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅ΡΠ½Ρ cubejs
ΠΈ QueryRenderer
, ΡΡΠΎΠ±Ρ Π²ΡΠ³ΡΡΠ·ΠΈΡΡ Π΄Π°Π½Π½ΡΠ΅, ΠΈ ΡΠΎΠ±ΠΈΡΠ°Π΅ΠΌ Π΄ΡΡΠ±ΠΎΡΠ΄:
ΠΠΎΠ΄ Π΄ΡΡΠ±ΠΎΡΠ΄Π°
import React from 'react';
import { LineChart, Line, XAxis, YAxis } from 'recharts';
import cubejs from '@cubejs-client/core';
import { QueryRenderer } from '@cubejs-client/react';
const cubejsApi = cubejs(
'YOUR-CUBEJS-API-TOKEN',
{ apiUrl: 'http://localhost:4000/cubejs-api/v1' },
);
export default () => {
return (
<QueryRenderer
query={{
measures: ['Logs.errorCount'],
timeDimensions: [{
dimension: 'Logs.createdAt',
dateRange: ['2019-01-01', '2019-01-07'],
granularity: 'day'
}]
}}
cubejsApi={cubejsApi}
render={({ resultSet }) => {
if (!resultSet) {
return 'Loading...';
}
return (
<LineChart data={resultSet.rawData()}>
<XAxis dataKey="Logs.createdAt"/>
<YAxis/>
<Line type="monotone" dataKey="Logs.errorCount" stroke="#8884d8"/>
</LineChart>
);
}}
/>
)
}
ΠΡΡ
ΠΎΠ΄Π½ΠΈΠΊΠΈ Π΄ΡΡΠ±ΠΎΡΠ΄Π° Π΄ΠΎΡΡΡΠΏΠ½Ρ Π½Π°
ΠΡΡΠΎΡΠ½ΠΈΠΊ: habr.com