éåžžãNginxã¯åçšè£œåãŸãã¯Prometheus + Grafanaãªã©ã®ãªãŒãã³ãœãŒã¹ã®ä»£æ¿è£œåã䜿çšããŠãNginxã®ããã©ãŒãã³ã¹ãç£èŠããã³åæããŸãã ããã¯ãç£èŠãŸãã¯ãªã¢ã«ã¿ã€ã åæã«é©ãããªãã·ã§ã³ã§ãããå±¥æŽåæã«ã¯ããŸã䟿å©ã§ã¯ãããŸããã äžè¬çãªãªãœãŒã¹ã§ã¯ãnginxãã°ããã®ããŒã¿éãæ¥éã«å¢å ããŠããããã倧éã®ããŒã¿ãåæããããã«ãããç¹æ®ãªãã®ã䜿çšããã®ãçã«ããªã£ãŠããŸãã
ãã®èšäºã§ã¯ãäŸãšããŠNginxã䜿çšããŠAthenaã䜿çšããŠãã°ãåæãããªãŒãã³ãœãŒã¹ã®cube.jsãã¬ãŒã ã¯ãŒã¯ã䜿çšããŠãã®ããŒã¿ããåæããã·ã¥ããŒããã³ã³ãã€ã«ããæ¹æ³ã説æããŸãã å®å
šãªãœãªã¥ãŒã·ã§ã³ã¢ãŒããã¯ãã£ã¯æ¬¡ã®ãšããã§ãã

TLïŒDR;
å®æããããã·ã¥ããŒããžã®ãªã³ã¯ ã
Fluentdã䜿çšããŠæ
å ±ãåéãã AWS Kinesis Data FirehoseãšAWS GlueãåŠçã«äœ¿çšãã AWS S3ãã¹ãã¬ãŒãžã«äœ¿çšããŸãã ãã®ãã³ãã«ã䜿çšãããšãnginxãã°ã ãã§ãªããä»ã®ã€ãã³ããä»ã®ãµãŒãã¹ã®ãã°ãä¿åã§ããŸãã ã¹ã¿ãã¯ã®äžéšãé¡äŒŒã®ããŒãã«çœ®ãæããããšãã§ããŸããããšãã°ãngidxããçŽæ¥kinesisã«ãã°ãæžã蟌ãã§ãfluentdããã€ãã¹ããããlogstashã䜿çšãããã§ããŸãã
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" "-"
ãããã¯è§£æã§ããŸãããJSONã§ãã°ã衚瀺ããããã«Nginxæ§æãä¿®æ£ããæ¹ãã¯ããã«ç°¡åã§ãïŒ
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ã®ããŒã¿ãçŽæ¥æäœã§ããããããã°ã1ãæã§ä¿åããã³åæã§ããŸãã èšäºã®åŸåã§ããã°ãæ£ããæããããã§åŠçããæ¹æ³ã説æããŸãããæåã«S3ã«ã¯ãªãŒã³ãã±ãããå¿
èŠã§ãããã®ãã±ããã«ã¯ä»ã«äœãä¿åãããŸããã ã¢ããã¯ãã¹ãŠã®å°åã§å©çšã§ããããã§ã¯ãªãããããã±ãããäœæããå°åãäºåã«æ€èšãã䟡å€ããããŸãã
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ã¹ããªãŒã ãäœæãã
Kinesis Firehoseã¯ãéžæãã圢åŒã§Nginxããåä¿¡ããããŒã¿ãS3ã«æžã蟌ã¿ãYYYY / MM / DD / HH圢åŒã®ãã£ã¬ã¯ããªã«åå²ããŸãã ããã¯ãããŒã¿ãèªã¿åããšãã«åœ¹ç«ã¡ãŸãã ãã¡ãããfluentdããS3ã«çŽæ¥æžã蟌ãããšãã§ããŸããããã®å Žåã¯JSONãèšè¿°ããå¿
èŠããããŸãããããã¯ãã¡ã€ã«ãµã€ãºã倧ããããéå¹ççã§ãã ãŸããPrestoDBãŸãã¯Athenaã䜿çšããå ŽåãJSONã¯æãé
ãããŒã¿åœ¢åŒã§ãã Kinesis Firehoseã³ã³ãœãŒã«ãéãã[é
ä¿¡ã¹ããªãŒã ã®äœæ]ãã¯ãªãã¯ããŠã[é
ä¿¡]ãã£ãŒã«ãã§[çŽæ¥PUT]ãéžæããŸãã

次ã®ã¿ãã§ããèšé²åœ¢åŒã®å€æã-ãæå¹ããéžæããèšé²ã®åœ¢åŒãšããŠãApache ORCããéžæããŸãã ããã€ãã®Owen O'Malleyã«ãããšãããã¯PrestoDBãšAthenaã«æé©ãªåœ¢åŒã§ãã å³ãšããŠãäžã§äœæããããŒãã«ã瀺ããŸãã kinesisã§ä»»æã®S3ãã±ãŒã·ã§ã³ãæå®ã§ããããšã«æ³šæããŠãã ãããè¡šã®ã¹ããŒã ã®ã¿ã䜿çšãããŸãã ãã ããå¥ã®S3ãã±ãŒã·ã§ã³ãæå®ãããšããã®ããŒãã«ãããããã®ã¬ã³ãŒãã®èªã¿åãã¯å€±æããŸãã

ã¹ãã¬ãŒãžãšä»¥åã«äœæãããã±ããã«S3ãéžæããŸãã Aws Glue Crawlerã¯ãåŸã§èª¬æããŸãããS3ãã±ããå
ã®ãã¬ãã£ãã¯ã¹ã®æäœæ¹æ³ãããããªãããã空ã®ãŸãŸã«ããŠããããšãéèŠã§ãã

æ®ãã®ãªãã·ã§ã³ã¯è² è·ã«å¿ããŠå€æŽã§ããŸããéåžžã¯ããã©ã«ãã䜿çšããŸãã S3å§çž®ã¯äœ¿çšã§ããŸããããORCã¯ããã©ã«ãã§ãã€ãã£ãå§çž®ã䜿çšããããšã«æ³šæããŠãã ããã
æµFlu
ãã°ã®ä¿åãšåä¿¡ãèšå®ããã®ã§ãéä¿¡ãèšå®ããå¿
èŠããããŸãã ç§ã¯Rubyã倧奜ããªã®ã§Fluentdã䜿çšããŸãããLogstashã䜿çšããããkinesisã«ãã°ãçŽæ¥éä¿¡ã§ããŸãã FluentdãµãŒããŒã¯ããã€ãã®æ¹æ³ã§èµ·åã§ããŸããDockerã«ã€ããŠèª¬æããŸããããã¯ã·ã³ãã«ã§äŸ¿å©ãªããã§ãã
ãŸããfluent.confæ§æãã¡ã€ã«ãå¿
èŠã§ãã äœæããŠãœãŒã¹ãè¿œå ããŸãã
ãã©ã¯ãŒãã¿ã€ã
ããŒã24224
ãã€ã³ã0.0.0.0
ããã§FluentdãµãŒããŒãèµ·åã§ããŸãã ããé«åºŠãªæ§æãå¿
èŠãªå Žåã Docker Hubã«ã¯ã€ã¡ãŒãžã®çµã¿ç«ãŠæ¹æ³ãªã©ã®è©³çŽ°ãªã¬ã€ãããããŸãã
$ 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
ãã¹ã䜿çšããŠãéä¿¡åã«ãã°ããã£ãã·ã¥ã/fluentd/log
ã ãããªãã§ãå®è¡ã§ããŸãããåèµ·åãããšãé床ã®åŽåã«ãã£ãŠãã£ãã·ã¥ããããã®ããã¹ãŠå€±ãããå¯èœæ§ããããŸãã ã©ã®ããŒãã䜿çšã§ããŸããããã©ã«ãã®FluentdããŒãã¯24224ã§ãã
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 firehoseãã©ã°ã€ã³ã䜿çšããŠãã°ã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>
ã¢ãã
ãã¹ãŠãæ£ããæ§æãããããã°ãããããšïŒããã©ã«ãã§ã¯ãKinesisã¯åä¿¡ããããŒã¿ã10åããšã«æžã蟌ã¿ãŸãïŒãS3ã«ãã°ãã¡ã€ã«ã衚瀺ãããŸãã Kinesis Firehoseã®ãç£èŠãã¡ãã¥ãŒã§ã¯ãS3ã«æžã蟌ãŸããããŒã¿ã®éãšãšã©ãŒã確èªã§ããŸãã KinesisããŒã«ã®S3ãã±ãããžã®æžã蟌ã¿ã¢ã¯ã»ã¹ãå¿ããã«äžããŠãã ããã Kinesisãäœãã解æã§ããªãã£ãå ŽåãKinesisã¯åããã±ããã«ãšã©ãŒãè¿œå ããŸãã
ããã§ãAthenaã®ããŒã¿ã確èªã§ããŸãã ãšã©ãŒãçºçããæ°ãããªã¯ãšã¹ããèŠã€ããŸãããã
SELECT * FROM "db_name"."table_name" WHERE status > 499 ORDER BY created_at DESC limit 10;
åãªã¯ãšã¹ãã®ãã¹ãŠã®ã¬ã³ãŒããã¹ãã£ã³ããŸã
ããã§ããã°ãåŠçãããORCã®S3ã«ã¹ã¿ãã¯ãããå§çž®ãããåæã®æºåãæŽããŸãã Kinesis Firehoseã¯ããããã1æéããšã«ãã£ã¬ã¯ããªã«é
眮ããŸãã ãã ããããŒãã«ã¯ããŒãã£ã·ã§ã³åãããŠããŸããããAthenaã¯ãŸããªäŸå€ãé€ããåã¯ãšãªã®åžžæããŒã¿ãããŒãããŸãã ããã¯2ã€ã®çç±ã§å€§ããªåé¡ã§ãã
- ããŒã¿éã¯çµ¶ããå¢å ããŠãããã¯ãšãªã®é床ãäœäžããŠããŸãã
- Athenaã¯ãã¹ãã£ã³ãããããŒã¿ã®éã«åºã¥ããŠè«æ±ããããªã¯ãšã¹ãããšã«æäœ10 MBãè«æ±ãããŸãã
ãããä¿®æ£ããããã«ãAWS Glue Crawlerã䜿çšããŸããããã¯ãS3ã®ããŒã¿ãã¹ãã£ã³ããGlue Metastoreã«ããŒãã£ã·ã§ã³æ
å ±ãèšé²ããŸãã ããã«ãããããŒãã£ã·ã§ã³ãAthenaã®ãªã¯ãšã¹ãã®ãã£ã«ã¿ãŒãšããŠäœ¿çšã§ãããªã¯ãšã¹ãã§æå®ããããã£ã¬ã¯ããªã®ã¿ãã¹ãã£ã³ããŸãã
Amazon Glue Crawlerãã«ã¹ã¿ãã€ãºãã
Amazon Glue Crawlerã¯ãS3ãã±ããå
ã®ãã¹ãŠã®ããŒã¿ãã¹ãã£ã³ããããŒãã£ã·ã§ã³ããŒãã«ãäœæããŸãã AWS Glueã³ã³ãœãŒã«ããGlue CrawlerãäœæããããŒã¿ãä¿åãããã±ãããè¿œå ããŸãã 1ã€ã®ã¯ããŒã©ãŒãè€æ°ã®ãã±ããã«äœ¿çšã§ããŸãããã®å Žåãæå®ãããããŒã¿ããŒã¹ã«ããã±ããã®ååãšäžèŽããååã®ããŒãã«ãäœæãããŸãã ãã®ããŒã¿ãåžžã«äœ¿çšããäºå®ãããå Žåã¯ãããŒãºã«åãããŠã¯ããŒã©ãŒã®èµ·åã¹ã±ãžã¥ãŒã«ã調æŽããŠãã ããã 1æéããšã«å®è¡ããããã¹ãŠã®ããŒãã«ã«å¯ŸããŠ1ã€ã®ã¯ããŒã©ãŒã䜿çšããŸãã
ããŒãã£ã·ã§ã³ããŒãã«
ã¯ããŒã©ãŒãæåã«èµ·åãããåŸãã¹ãã£ã³ãããåãã±ããã®ããŒã¿ããŒã¹ãèšå®ã§æå®ãããããŒã¿ããŒã¹ã«è¡šç€ºãããŸãã 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' );
ãã®ã¯ãšãªã¯ã2019幎4æ8æ¥ã®åå6æããåå7æãŸã§ã«åä¿¡ãããã¹ãŠã®ã¬ã³ãŒããéžæããŸãã ããããããã¯ããŒãã£ã·ã§ã³åãããŠããªãããŒãã«ããèªã¿åããããã¯ããã«å¹ççã§ããïŒ ã¿ã€ã ã¹ã¿ã³ãã§ãã£ã«ã¿ãªã³ã°ããŠåãã¬ã³ãŒããèŠã€ããŠéžæããŸãããïŒ

ããŒã¿ã»ããäžã®3.59ç§ããã³244.34ã¡ã¬ãã€ãã®ããŒã¿ããã°ã¯1é±éã®ã¿ã§ãã ããŒãã£ã·ã§ã³ããšã«ãã£ã«ã¿ãŒãè©ŠããŠã¿ãŸãããã

å°ãé«éã§ãããæãéèŠãªã®ã¯ãããã1.23ã¡ã¬ãã€ãã®ããŒã¿ã§ãïŒ äŸ¡æ Œèšå®ã§ãªã¯ãšã¹ãããšã«æäœ10ã¡ã¬ãã€ãããªããã°ãã¯ããã«å®ããªããŸãã ãããããšã«ããã¯ããã«åªããŠããã倧èŠæš¡ãªããŒã¿ã»ããã§ã¯ãéãã¯ã¯ããã«å°è±¡çã§ãã
Cube.jsã䜿çšããŠããã·ã¥ããŒããæ§ç¯ãã
ããã·ã¥ããŒããäœæããã«ã¯ãCube.jsåæãã¬ãŒã ã¯ãŒã¯ã䜿çšããŸãã ããªãã®æ°ã®æ©èœããããŸããã2ã€ã«èå³ããããŸããããŒãã£ã·ã§ã³ã®ãã£ã«ã¿ãŒãèªåçã«äœ¿çšããæ©èœãšããŒã¿ã®äºåéèšã§ãã Javascriptã§èšè¿°ãããããŒã¿ã¹ããŒãã䜿çšããŠãSQLãçæããããŒã¿ããŒã¹ã¯ãšãªãå®è¡ããŸãã å¿
èŠãªã®ã¯ãããŒã¿ã¹ããŒãã§ããŒãã£ã·ã§ã³ãã£ã«ã¿ãŒã䜿çšããæ¹æ³ã瀺ãããšã ãã§ãã
æ°ããã¢ããªã±ãŒã·ã§ã³Cube.jsãäœæããŸãããã æ¢ã«AWS-stackã䜿çšããŠããããããããã€ã«Lambdaã䜿çšããã®ãè«ççã§ãã HerokuãŸãã¯Dockerã§Cube.jsããã¯ãšã³ãããã¹ãããäºå®ã®å Žåãçæã«ãšã¯ã¹ãã¬ã¹ãã³ãã¬ãŒãã䜿çšã§ããŸãã ããã¥ã¡ã³ãã¯ä»ã®ãã¹ãã£ã³ã°æ¹æ³ã«ã€ããŠèª¬æããŸã ã
$ npm install -g cubejs-cli $ cubejs create nginx-log-analytics -t serverless -d athena
cube.jsã§ããŒã¿ããŒã¹ãžã®ã¢ã¯ã»ã¹ãæ§æããã«ã¯ãç°å¢å€æ°ã䜿çšãããŸãã ãžã§ãã¬ãŒã¿ãŒã¯ã Athenaã®ããŒãæå®ã§ãã.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` } } });
ããã§ã¯ã FILTER_PARAMSå€æ°ã䜿çšããŠãããŒãã£ã·ã§ã³ãã£ã«ã¿ãŒã䜿çšããŠSQLã¯ãšãªãçæããŸãã
ãŸããããã·ã¥ããŒãã«è¡šç€ºããã¡ããªãã¯ãšãã©ã¡ãŒã¿ãŒãæå®ããäºåéèšãæå®ããŸãã 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ããã¯ãšã³ãã¯ãäžè¬çãªããã³ããšã³ããã¬ãŒã ã¯ãŒã¯çšã®REST APIãšã¯ã©ã€ã¢ã³ãã©ã€ãã©ãªã®ã»ãããæäŸããŸã ã ã¯ã©ã€ã¢ã³ãã®ReactããŒãžã§ã³ã䜿çšããŠããã·ã¥ããŒããæ§ç¯ããŸãã Cube.jsã¯ããŒã¿ã®ã¿ãæäŸãããããèŠèŠåã®ããã®ã©ã€ãã©ãªãå¿
èŠã§ã-ç§ã¯rechartsã奜ãã§ãããã©ãã§ã䜿çšã§ããŸãã
Cube.jsãµãŒããŒã¯ãå¿
èŠãªã¡ããªãã¯ã瀺ãJSON圢åŒã®ãªã¯ãšã¹ããåãå
¥ããŸãã ããšãã°ãNginxãæ¥ããšã«äžãããšã©ãŒã®æ°ãèšç®ããã«ã¯ã次ã®ãªã¯ãšã¹ããéä¿¡ããå¿
èŠããããŸãã
{ "measures": ["Logs.errorCount"], "timeDimensions": [ { "dimension": "Logs.createdAt", "dateRange": ["2019-01-01", "2019-01-07"], "granularity": "day" } ] }
NPMãä»ããŠCube.jsã¯ã©ã€ã¢ã³ããšReactã³ã³ããŒãã³ãã©ã€ãã©ãªãã€ã³ã¹ããŒã«ããŸãã
$ 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> ); }} /> ) }
ããã·ã¥ããŒãã®ãœãŒã¹ã¯CodeSandboxã§å
¥æã§ããŸãã