ืืืจื ืืื, ืืืฆืจืื ืืกืืจืืื ืื ืืืืคืืช ืืืื ืืช ืฉื ืงืื ืคืชืื, ืืืื Prometheus + Grafana, ืืฉืืฉืื ืื ืืืืจ ืื ืืชืื ืืคืขืืื ืฉื Nginx. ืืืื ืืคืฉืจืืช ืืืื ืื ืืืืจ ืื ืื ืืชืื ืืืื ืืืช, ืื ืื ืืืื ื ืืื ืื ืืชืื ืืืกืืืจื. ืืื ืืฉืื ืคืืคืืืจื, ื ืคื ืื ืชืื ืื ืืืืื ื 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
ืืืื ื ืืฆืืจ ืืืื ืืืชื ื ืขืืืจ ืืืื ืื. ืื ื ืืืฅ ืื ืืืชืืื ืืื ืืงืจืืื ืื โโืืชื ืืชืื ื ืื ืืืฉืชืืฉ ื-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 ืืคืืจืื ืื ืืืจ, ืืชืืืง ืืืชื ืืกืคืจืืืช ืืคืืจืื YYYY/MM/DD/HH. ืื ืืืื ืฉืืืืฉื ืืขืช ืงืจืืืช ื ืชืื ืื. ืืคืฉืจ ืืืืื ืืืชืื ืืฉืืจืืช ื-S3 ื-fluentd, ืืื ืืืงืจื ืืื ืชืฆืืจืื ืืืชืื JSON, ืืื ืื ืืขืื ืืืื ืืืืื ืืืืื ืฉื ืืงืืฆืื. ืื ืืกืฃ, ืืขืช ืฉืืืืฉ ื-PrestoDB ืื Athena, JSON ืืื ืคืืจืื ืื ืชืื ืื ืืืืื ืืืืชืจ. ืื ืคืชืื ืืช ืงืื ืกืืืช Kinesis Firehose, ืืืฆื ืขื "ืฆืืจ ืืจื ืืฉืืื", ืืืจื "PUT ืืฉืืจ" ืืฉืื "ืืกืืจื":
ืืืจืืืกืืื ืืืื, ืืืจ "ืืืจืช ืคืืจืื ืืืงืืื" - "ืืืคืขื" ืืืืจ "Apache ORC" ืืคืืจืื ืืืงืืื. ืืคื ืืื ืืืงืจืื
ืื ื ืืืืจืื ืืช S3 ืืืืกืื ืืืช ืืืื ืฉืืฆืจื ื ืงืืื ืืื. Aws Glue Crawler, ืขืืื ืืืืจ ืืขื ืืืืฉื, ืื ืืืื ืืขืืื ืขื ืงืืืืืืช ืืืื S3, ืืืื ืืฉืื ืืืฉืืืจ ืืืชื ืจืืง.
ื ืืชื ืืฉื ืืช ืืช ืืืคืฉืจืืืืช ืื ืืชืจืืช ืืืชืื ืืขืืืก ืฉืื; ืื ื ืืืจื ืืื ืืฉืชืืฉ ืืืจืืจืช ืืืืื. ืฉืื ืื ืฉืืืืกื ืฉื S3 ืืื ื ืืืื ื, ืื ORC ืืฉืชืืฉืช ืืืืืกื ืืงืืจืืช ืืืจืืจืช ืืืื.
ืคืืืื ื
ืืขืช, ืืืืจ ืฉืืืืจื ื ืืืกืื ืืงืืื ืฉื ืืืื ืื, ืขืืื ื ืืืืืืจ ืืช ืืฉืืืื. ืื ืื ื ื ืฉืชืืฉ
ืจืืฉืืช, ืื ืื ื ืฆืจืืืื ืืช ืงืืืฅ ืืชืฆืืจื fluent.conf. ืฆืืจ ืืืชื ืืืืกืฃ ืืงืืจ:
ืืฆืืื 24224
ืืืื 0.0.0.0
ืขืืฉืื ืืชื ืืืื ืืืคืขืื ืืช ืฉืจืช Fluent. ืื ืืชื ืฆืจืื ืชืฆืืจื ืืชืงืืืช ืืืชืจ, ืขืืืจ ืื
$ 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.
ืืขืช, ืืืฉืจ ืืฉ ืื ื ืืช Fluent ืคืืขืืช, ืื ื ืืืืืื ืืฉืืื ืืฉื ืืืื ื 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 ืืฆืืจื ืฉืื ื, ืืชื ืืืื ืืืฉืชืืฉ ืืงืืืฆื ืืืื, ืื ืืฉ ื-Fuentd
ืืืื ื ืืกืืฃ ืืช ื ืืชืื ืืืืื ืฉืืืืืจ ืืขืื ืืชืฆืืจืช Fluent:
<filter YOUR-NGINX-TAG.*>
@type parser
key_name log
emit_invalid_record_to_error false
<parse>
@type json
</parse>
</filter>
ืืฉืืืืช ืืืื ืื ืืงืื ืืืก ืืืืฆืขืืช
<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, ืืื ืื ืฉืืืืืช. ืื ืชืฉืื ืืชืช ืืืฉืช ืืชืืื ืืืื S3 ืืชืคืงืื Kinesis. ืื Kinesis ืื ืืฆืืื ืื ืชื ืืฉืื, ืืื ืืืกืืฃ ืืช ืืฉืืืืืช ืืืืชื ืืื.
ืขืืฉืื ืืชื ืืืื ืืืฆืื ืืช ืื ืชืื ืื ืืืชื ื. ืืื ื ืืฆื ืืช ืืืงืฉืืช ืืืืจืื ืืช ืฉืขืืืจื ืืืืจื ื ืฉืืืืืช:
SELECT * FROM "db_name"."table_name" WHERE status > 499 ORDER BY created_at DESC limit 10;
ืกืจืืงืช ืื ืืจืฉืืืืช ืขืืืจ ืื ืืงืฉื
ืืขืช ืืืืื ืื ืฉืื ื ืขืืจื ืขืืืื ืืืืืกื ื ื-S3 ื-ORC, ืืืืกืื ืืืืื ืื ืื ืืชืื. Kinesis Firehose ืืคืืื ืืจืื ื ืืืชื ืืกืคืจืืืช ืขืืืจ ืื ืฉืขื. ืขื ืืืช, ืื ืขืื ืืืืื ืืื ื ืืืืืงืช, Athena ืชืืขื ื ืชืื ืื ืืื ืืืื ืื ืขื ืื ืืงืฉื, ืืืขื ืืจืืืื ื ืืืจืื. ืื ืืขืื ืืืืื ืืฉืชื ืกืืืืช:
- ื ืคื ืื ืชืื ืื ืืื ืื ืืืื, ืืืื ืืช ืืฉืืืืชืืช;
- ืืชื ื ืืืืืืช ืขื ืกืื ื ืคื ืื ืชืื ืื ืฉื ืกืจืงื, ืขื ืืื ืืืื ืฉื 10 MB ืืื ืืงืฉื.
ืืื ืืชืงื ืืืช, ืื ื ืืฉืชืืฉืื ื-AWS Glue Crawler, ืืฉืจ ืืกืจืืง ืืช ืื ืชืื ืื ื-S3 ืืืืชืื ืืช ืคืจืื ืืืืืฆื ื-Glue Metastore. ืื ืืืคืฉืจ ืื ื ืืืฉืชืืฉ ืืืืืฆืืช ืืืกื ื ืืขืช โโืฉืืืืชืช Athena, ืืืื ืืกืจืืง ืจืง ืืช ืืกืคืจืืืช ืฉืฆืืื ื ืืฉืืืืชื.
ืืืืจืช Amazon Glue Crawler
Amazon Glue Crawler ืกืืจืง ืืช ืื ืื ืชืื ืื ืืืื S3 ืืืืฆืจ ืืืืืืช ืขื ืืืืฆืืช. ืฆืืจ ืกืืจืง ืืืง ืืืกืืฃ ืืืืง ืฉื AWS ืืืืกืฃ ืืื ืฉืื ืืชื ืืืืกื ืืช ืื ืชืื ืื. ื ืืชื ืืืฉืชืืฉ ืืกืืจืง ืืื ืขืืืจ ืืกืคืจ ืืืืื, ืืืืงืจื ืื ืืื ืืฆืืจ ืืืืืืช ืืืกื ืื ืชืื ืื ืฉืฆืืื ืขื ืฉืืืช ืืชืืืืื ืืช ืฉืืืช ืืืืืื. ืื ืืชื ืืชืื ื ืืืฉืชืืฉ ืื ืชืื ืื ืืื ืืืืคื ืงืืืข, ืืงืคื ืืืืืืจ ืืช ืืื ืืืื ืื ืฉื ืืืฉืงื ืฉื 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 ื-XNUMX ืืืคืจืื, XNUMX. ืืื ืืื ืื ืืขืื ืืืชืจ ืืกืชื ืงืจืืื ืืืืื ืื ืืืืืงืช? ืืืื ื ืืจืจ ืื ืืืจ ืืช ืืืชื ืจืฉืืืืช, ืื ืกื ื ืืืชื ืืคื ืืืชืืช ืืื:
3.59 ืฉื ืืืช ื-244.34 ืืื-ืืืื ืฉื ื ืชืื ืื ืืืขืจื ื ืชืื ืื ืขื ืจืง ืฉืืืข ืฉื ืืืื ืื. ืืื ื ื ืกื ืืกื ื ืืคื ืืืืฆื:
ืงืฆืช ืืืชืจ ืืืจ, ืืื ืืื ืืฉืื - ืจืง 1.23 ืืื ืืืื ืฉื ื ืชืื ืื! ืื ืืืื ืืจืื ืืืชืจ ืืื ืืืืื ืืืื ืืืื 10 ืืื-ืืืื ืืื ืืงืฉื ืืชืืืืจ. ืืื ืื ืขืืืื ืืจืื ืืืชืจ ืืื, ืืืืขืจืื ื ืชืื ืื ืืืืืื ืืืืื ืืืื ืืจืื ืืืชืจ ืืจืฉืื.
ืื ืืืช ืืื ืืืืื ืื ืืืืฆืขืืช Cube.js
ืืื ืืืจืืื ืืช ืืื ืืืืืื ืื, ืื ื ืืฉืชืืฉืื ืืืกืืจืช ืืื ืืืืืช Cube.js. ืืฉ ืื ืื ืืจืื ืคืื ืงืฆืืืช, ืืื ืื ืื ื ืืขืื ืืื ืื ืืฉืชืืื: ืืืืืืช ืืืฉืชืืฉ ืืืืคื ืืืืืืื ืืืกื ื ื ืืืืฆืืช ืืืฆืืืจื ืืจืืฉ ืฉื ื ืชืื ืื. ืืื ืืฉืชืืฉ ืืกืืืืช ื ืชืื ืื
ืืืื ื ืืฆืืจ ืืืฉืื Cube.js ืืืฉ. ืืืืืื ืฉืื ื ืืืจ ืืฉืชืืฉืื ืืขืจืืืช AWS, ืื ืืืืื ื ืืืฉืชืืฉ ื- Lambda ืืฆืืจื ืคืจืืกื. ืืชื ืืืื ืืืฉืชืืฉ ืืชืื ืืช ืืืงืกืคืจืก ืืืฆืืจืช ืื ืืชื ืืชืื ื ืืืจื ืืช ื-Cube.js backend ื-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 ืชืืฆืืจ ืืืืืืช ื ืืกืคืืช ืขื ื ืชืื ืื ืืฆืืืจืื ืืจืืฉ ืืืขืืื ืืช ืื ืชืื ืื ืืืืคื ืืืืืืื ืืฉืื ืืืืขืื. ืื ืื ืจืง ืืืืฅ ืืช ืืฉืืืืชืืช, ืืื ืื ืืคืืืช ืืช ืขืืืช ืืฉืืืืฉ ืืืชื ื.
ืืืื ื ืืกืืฃ ืืช ืืืืืข ืืื ืืงืืืฅ ืกืืืืช ืื ืชืื ืื:
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 backend ืืกืคืง
ืฉืจืช 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>
);
}}
/>
)
}
ืืงืืจืืช ืืื ืืืืืื ืื ืืืื ืื ื
ืืงืืจ: www.habr.com