From fb0c7d775935aaeba8fb2be0086654b82918514a Mon Sep 17 00:00:00 2001 From: kranian Date: Mon, 24 Jun 2019 17:40:55 +0900 Subject: [PATCH 01/77] :bug: gray theme topology chart fixed the invisible tooltip #226 --- src/Theme.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Theme.css b/src/Theme.css index b578e27..a3150e4 100644 --- a/src/Theme.css +++ b/src/Theme.css @@ -78,6 +78,10 @@ html.theme-gray { stroke: #deffba; } +/* THEME, topology-chart.css */ +.theme-gray .topology-wrapper .tooltip-group{ + color: #111; +} /* THEME, Login.css */ .theme-gray .login-wrapper .login-box { From a531391926c2e128941b26e4d5bfe9f374810a63 Mon Sep 17 00:00:00 2001 From: david100gom Date: Mon, 24 Jun 2019 18:07:35 +0900 Subject: [PATCH 02/77] modify init XLog load data issue. --- src/components/Paper/XLog/XLog.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Paper/XLog/XLog.js b/src/components/Paper/XLog/XLog.js index 67128a5..3b897f5 100644 --- a/src/components/Paper/XLog/XLog.js +++ b/src/components/Paper/XLog/XLog.js @@ -144,7 +144,7 @@ class XLog extends Component { this.lastPageCnt = this.props.pageCnt; this.draw(this.props.data.newXLogs, this.props.filter); } else { - this.draw(this.props.data.newXLogs, this.props.filter); + this.redraw(this.props.filter); } if (this.props.filter && JSON.stringify(prevProps.filter) !== JSON.stringify(this.props.filter)) { From cc10381a945f7408891e2aacff95bd6f36bd0984 Mon Sep 17 00:00:00 2001 From: kranian Date: Mon, 24 Jun 2019 18:49:39 +0900 Subject: [PATCH 03/77] :zap: docker build file adding --- docker/.env | 1 + docker/Dockerfile | 9 +++++++ docker/HOWTO-Build.md | 13 ++++++++++ docker/build.sh | 6 +++++ docker/default.conf | 50 +++++++++++++++++++++++++++++++++++++++ docker/docker-compose.yml | 7 ++++++ docker/push.sh | 4 ++++ 7 files changed, 90 insertions(+) create mode 100644 docker/.env create mode 100644 docker/Dockerfile create mode 100644 docker/HOWTO-Build.md create mode 100755 docker/build.sh create mode 100644 docker/default.conf create mode 100644 docker/docker-compose.yml create mode 100755 docker/push.sh diff --git a/docker/.env b/docker/.env new file mode 100644 index 0000000..ae99389 --- /dev/null +++ b/docker/.env @@ -0,0 +1 @@ +PAPER_VERSION=2.5.0 diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..e309044 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,9 @@ +FROM nginx:1.17.0-alpine +## url setting +LABEL maintainer="yosong.heo@gmail.com" +ARG INSTALL_URL=https://github.com/scouter-contrib/scouter-paper/releases/download/${PAPER_VERSION:-2.5.0}/scouter-paper-v${PAPER_VERSION:-2.5.0}.zip +RUN mkdir -p /var/www; +COPY default.conf /etc/nginx/conf.d/default.conf +## install +WORKDIR /var/www +RUN apk add -U tzdata wget unzip;cp /usr/share/zoneinfo/Asia/Seoul /etc/localtime; wget ${INSTALL_URL};unzip scouter-paper-v${PAPER_VERSION:-2.5.0}.zip diff --git a/docker/HOWTO-Build.md b/docker/HOWTO-Build.md new file mode 100644 index 0000000..df242a2 --- /dev/null +++ b/docker/HOWTO-Build.md @@ -0,0 +1,13 @@ +## 1. 빌드 요구 사항 + - docker version + - Docker version 18.09.6 이상 +### 1.2 빌드 +- .env 파일에 Scouter Paper 릴리스 버전 변경 후 +``` +$./build.sh +``` +### 1.3 dockerhub 저장소에 Push + +``` +$./push.sh +``` diff --git a/docker/build.sh b/docker/build.sh new file mode 100755 index 0000000..6c7547a --- /dev/null +++ b/docker/build.sh @@ -0,0 +1,6 @@ +#!/bin/bash +source .env + +echo ">>> build paper version $PAPER_VERSION" +export $PAPER_VERSION +docker build -t scouterapm/scouter-paper:$PAPER_VERSION . diff --git a/docker/default.conf b/docker/default.conf new file mode 100644 index 0000000..589eec3 --- /dev/null +++ b/docker/default.conf @@ -0,0 +1,50 @@ +server { + listen 80; + server_name localhost; + charset utf-8; + + #charset koi8-r; + #access_log /var/log/nginx/host.access.log main; + + location / { + root /var/www; + index index.html index.htm; + } + + #error_page 404 /404.html; + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } + + + location ~* /\.(?!well-known\/) + { + deny all; + } + + # Prevent clients from accessing to backup/config/source files + location ~* (?:\.(?:bak|conf|dist|fla|in[ci]|log|psd|sh|sql|sw[op])|~)$ + { + deny all; + } + + #Prevent to log favicon.ico + location = /favicon.ico + { + access_log off; + log_not_found off; + } + + #Prevent to log robots.txt + location = /robots.txt + { + access_log off; + log_not_found off; + } + +} + diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000..6b3ad07 --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,7 @@ +version: '3.2' +services: + scouter-paper: + image : scouterapm/scouter-paper:2.5.0 + restart : always + ports: + - 8080:80 \ No newline at end of file diff --git a/docker/push.sh b/docker/push.sh new file mode 100755 index 0000000..e721802 --- /dev/null +++ b/docker/push.sh @@ -0,0 +1,4 @@ +#!/bin/bash +source .env +echo ">>> build paper version $PAPER_VERSION" +docker push scouterapm/scouter-paper:$PAPER_VERSION From 715241c12287b623ae406f8ff5a07c165dc3fd27 Mon Sep 17 00:00:00 2001 From: kranian Date: Tue, 25 Jun 2019 16:36:06 +0900 Subject: [PATCH 04/77] :zap: travis ci adding (test) --- .travis.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..631d582 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,10 @@ +dist: xenial +language: node_js +before_install: + - npm install +script : + - npm build +cache: npm +branches: + only: + - master \ No newline at end of file From ddccda4e84b5591187e2eea69e59819a38723f03 Mon Sep 17 00:00:00 2001 From: kranian Date: Tue, 25 Jun 2019 16:42:26 +0900 Subject: [PATCH 05/77] :zap: travis ci adding (test) --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 631d582..109bb4e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,4 +7,5 @@ script : cache: npm branches: only: - - master \ No newline at end of file + - master + - development \ No newline at end of file From 08b265b599f278ec003d578bcef4861d0dbe4cf5 Mon Sep 17 00:00:00 2001 From: kranian Date: Tue, 25 Jun 2019 17:06:24 +0900 Subject: [PATCH 06/77] :zap: travis ci adding (test: development) --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 109bb4e..ca42f30 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,5 +7,4 @@ script : cache: npm branches: only: - - master - development \ No newline at end of file From 82a438ebc22f6e3b584ec975f82537ff994b33b6 Mon Sep 17 00:00:00 2001 From: kranian Date: Tue, 25 Jun 2019 17:19:14 +0900 Subject: [PATCH 07/77] :zap: travis ci adding (test: development 2) --- .travis.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ca42f30..deed5ce 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,14 @@ dist: xenial language: node_js +node_js: + - "stable" before_install: - npm install script : - npm build -cache: npm +cache: + directories: + - "node_modules" branches: only: - development \ No newline at end of file From 67796813e00df3add8b06a3b78ac7ad234b1f98d Mon Sep 17 00:00:00 2001 From: kranian Date: Tue, 25 Jun 2019 17:26:45 +0900 Subject: [PATCH 08/77] :zap: travis ci adding (test: development 3) --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index deed5ce..fecc5e5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ node_js: before_install: - npm install script : - - npm build + - npm run-script build cache: directories: - "node_modules" From ba4179ee64ae9f3eded5a42a4b4b5551e9a0d63a Mon Sep 17 00:00:00 2001 From: kranian Date: Wed, 26 Jun 2019 11:20:35 +0900 Subject: [PATCH 09/77] :zap: travis ci adding (test: development deploy 1) --- .travis.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fecc5e5..d48183a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,9 +6,13 @@ before_install: - npm install script : - npm run-script build +after_script: + - tar cvf $SCOUTER_PAPER-COMPRESS-NAME build/ cache: directories: - "node_modules" branches: only: - - development \ No newline at end of file + - development +notifications: + slack: scouterapm:fJndRYgbQA0uTLXiEqxZJhUU \ No newline at end of file From e32f96424cb8344ea605583a1023932818e51356 Mon Sep 17 00:00:00 2001 From: kranian Date: Wed, 26 Jun 2019 14:35:36 +0900 Subject: [PATCH 10/77] :zap: travis ci adding (test: development deploy 2) --- .travis.yml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index d48183a..9c6f13e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,13 +6,24 @@ before_install: - npm install script : - npm run-script build -after_script: +after_success: - tar cvf $SCOUTER_PAPER-COMPRESS-NAME build/ +before_deploy: + - apt-get install sshpass + - apt-get install openssh-client +after_deploy : + - echo "$DEMO_DEPLOY_KEY" | tr -d '\r' > $DEMO_DELOY_FILE + - scp -i $DEMO_DELOY_FILE ./$SCOUTER_PAPER-COMPRESS-NAME $DEMO_SERVER_USER@$DEMO_SERVER_NODE + - ssh -T $DEMO_SERVER_USER@$DEMO_SERVER_NODE "/home/ec2-user/deploy.sh $SCOUTER_PAPER-COMPRESS-NAME" cache: directories: - "node_modules" branches: only: - development + - master notifications: - slack: scouterapm:fJndRYgbQA0uTLXiEqxZJhUU \ No newline at end of file + slack: scouterapm:fJndRYgbQA0uTLXiEqxZJhUU + +#: construction_worker: +# job life cycle explain : https://docs.travis-ci.com/user/job-lifecycle/ \ No newline at end of file From 410a5217f0d7292dcbe5f29ba8f9abbbd82a4323 Mon Sep 17 00:00:00 2001 From: kranian Date: Wed, 26 Jun 2019 14:38:10 +0900 Subject: [PATCH 11/77] :construction_worker: travis ci adding (test: development deploy 3) --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9c6f13e..2211579 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ before_deploy: after_deploy : - echo "$DEMO_DEPLOY_KEY" | tr -d '\r' > $DEMO_DELOY_FILE - scp -i $DEMO_DELOY_FILE ./$SCOUTER_PAPER-COMPRESS-NAME $DEMO_SERVER_USER@$DEMO_SERVER_NODE - - ssh -T $DEMO_SERVER_USER@$DEMO_SERVER_NODE "/home/ec2-user/deploy.sh $SCOUTER_PAPER-COMPRESS-NAME" + - ssh -i $DEMO_DELOY_FILE -T $DEMO_SERVER_USER@$DEMO_SERVER_NODE "/home/ec2-user/deploy.sh $SCOUTER_PAPER-COMPRESS-NAME" cache: directories: - "node_modules" From 7e584f2610ac746af41dcb9aae6920971bdafe27 Mon Sep 17 00:00:00 2001 From: kranian Date: Wed, 26 Jun 2019 14:39:17 +0900 Subject: [PATCH 12/77] :construction_worker: travis ci adding (test: development deploy 3) --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2211579..8880770 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,10 +8,8 @@ script : - npm run-script build after_success: - tar cvf $SCOUTER_PAPER-COMPRESS-NAME build/ -before_deploy: - apt-get install sshpass - apt-get install openssh-client -after_deploy : - echo "$DEMO_DEPLOY_KEY" | tr -d '\r' > $DEMO_DELOY_FILE - scp -i $DEMO_DELOY_FILE ./$SCOUTER_PAPER-COMPRESS-NAME $DEMO_SERVER_USER@$DEMO_SERVER_NODE - ssh -i $DEMO_DELOY_FILE -T $DEMO_SERVER_USER@$DEMO_SERVER_NODE "/home/ec2-user/deploy.sh $SCOUTER_PAPER-COMPRESS-NAME" From d2f03f8b5dbde63a1ac2dd61d54adf1af8588eeb Mon Sep 17 00:00:00 2001 From: kranian Date: Wed, 26 Jun 2019 15:01:36 +0900 Subject: [PATCH 13/77] :construction_worker: travis ci adding (test: development deploy 4) --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 8880770..1cc1994 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,9 @@ after_success: - apt-get install sshpass - apt-get install openssh-client - echo "$DEMO_DEPLOY_KEY" | tr -d '\r' > $DEMO_DELOY_FILE + - echo "$DEMO_DEPLOY_KEY" | tr -d '\r' > ~/.ssh/id_rsa + - cat ~/.ssh/id_rsa + - ssh-keyscan -H $DEMO_SERVER_NODE >> ~/.ssh/known_hosts - scp -i $DEMO_DELOY_FILE ./$SCOUTER_PAPER-COMPRESS-NAME $DEMO_SERVER_USER@$DEMO_SERVER_NODE - ssh -i $DEMO_DELOY_FILE -T $DEMO_SERVER_USER@$DEMO_SERVER_NODE "/home/ec2-user/deploy.sh $SCOUTER_PAPER-COMPRESS-NAME" cache: From 5ada1043fa6b8993a67f243dd565c5400daefd85 Mon Sep 17 00:00:00 2001 From: david100gom Date: Wed, 26 Jun 2019 18:37:14 +0900 Subject: [PATCH 14/77] remove duplication txid data --- src/components/Paper/XLog/Profiler/Profiler.js | 6 +++++- src/components/Paper/XLog/XLog.js | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/Paper/XLog/Profiler/Profiler.js b/src/components/Paper/XLog/Profiler/Profiler.js index c6a12c0..125a413 100644 --- a/src/components/Paper/XLog/Profiler/Profiler.js +++ b/src/components/Paper/XLog/Profiler/Profiler.js @@ -301,6 +301,10 @@ class Profiler extends Component { return; } + // remove duplication txid + var arrTxid = (filtered.map(x => x.txid).toString()).split(","); + var strTxid = arrTxid.reduce(function(a,b){ if(a.indexOf(b) < 0) a.push(b); return a;},[]); + let date = moment(new Date(x1)).format("YYYYMMDD"); this.props.setControlVisibility("Loading", true); @@ -308,7 +312,7 @@ class Profiler extends Component { jQuery.ajax({ method: "GET", async: true, - url: getHttpProtocol(this.props.config) + '/scouter/v1/xlog-data/' + date + '/multi/' + filtered.map(x => x.txid).toString(), + url: getHttpProtocol(this.props.config) + '/scouter/v1/xlog-data/' + date + '/multi/' + strTxid, xhrFields: getWithCredentials(that.props.config), beforeSend: function (xhr) { setAuthHeader(xhr, that.props.config, getCurrentUser(that.props.config, that.props.user)); diff --git a/src/components/Paper/XLog/XLog.js b/src/components/Paper/XLog/XLog.js index 3b897f5..99fdb89 100644 --- a/src/components/Paper/XLog/XLog.js +++ b/src/components/Paper/XLog/XLog.js @@ -142,8 +142,10 @@ class XLog extends Component { this.redraw(this.props.filter); } else if (this.lastPastTimestamp === this.props.pastTimestamp && this.lastPageCnt !== this.props.pageCnt) { this.lastPageCnt = this.props.pageCnt; + this.clear(); this.draw(this.props.data.newXLogs, this.props.filter); } else { + this.clear(); this.redraw(this.props.filter); } From 8a59d7bd754a6d8819317554bf4d65f17d6f5136 Mon Sep 17 00:00:00 2001 From: kranian Date: Wed, 26 Jun 2019 19:03:39 +0900 Subject: [PATCH 15/77] :construction_worker: travis ci action deploy --- .deploy.enc | 23 +++++++++++++++++++++++ .travis.yml | 23 +++++++++++++---------- 2 files changed, 36 insertions(+), 10 deletions(-) create mode 100644 .deploy.enc diff --git a/.deploy.enc b/.deploy.enc new file mode 100644 index 0000000..1e13027 --- /dev/null +++ b/.deploy.enc @@ -0,0 +1,23 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA7Rynr4EU1iPAAFxj/hTlIVs7+6bCwByL/ZMBjqYBAJHj9JVn7i/ruefuCJY2 +KV8nPHr0DxIXbwfCbucHnQAGQOL0wH606b5Pjjw869Iv9xXGGU5dhVWhG370IRL7ijBcEGZNPfC1 +Pyv5OVV7kIR9tZ1E5eft7Co1VGdRqxTBoe9OHRz0kanEnmfAtSg3lqNaqP34upBSgPmmXTF02diR +CWkhBCEr2r/0EadN6L7XXPYIe9+shF3BYKmWzvlm75tJUSXUUvMWHTmNI5BQG+joXCgoZtTveuTt +IluPvE+BpkDHDsZ1fUIinOWkK8qSgcXtcpTxOt9cjQB0JgqkDvbzEQIDAQABAoIBAFoukeCAecpQ +YpBAk49ppxQevRuje2LR+LSDGw6EONFcq5S01oiTFVbpzXDaoNLva97m7Lhmqx8BloPTvuOJxNcc ++vXTuz9DsMJqSslucevKDkRFt/Akd1gb2wJRU/ZlGj7qiepLCMR0FyAlUQ/QNHfEzxWaNrWiABKQ +EVG+XT8XJ+qvWBwjc1n/rk3vN9nFZznIEozB0EefloaDQAepEw9BENvpOUcUObSbTW7fpPjb7jXw +0rrxkFi5gaMvr4R6VcwuzyuC4NpqDJAoI11pqwqWHhpHjL5g9kWRG2BiKNG4P8LHqqU/ERdjYhZh +dY+eRvlP4y2A0QaDWQOTH4d9JYECgYEA+/2hAlvZBDbJiJpWTot5mw2sWjEAICM/vvFCakAkCExC +MNFNDtKyA+TpHt3+M77OhRMG0MUredqPNDrHiygo4C1kmDi6ztNs1Hl8/QsIkz2q3H5GRh5ZebhS +vnQqnPtUHvh9w12m1aQulNiS59SzCxd2iRyI1SVhb9g0liFOzxkCgYEA8OJsiElf8l2apFaT+2Wc +pLooY/eONtRJ/wWfUSYR1/YcRsSbFHZ8pSF0pbRThyniV2mYs9voz+6tg95QG55Dna1+xXnbeKz2 +Hx6oah4IR5nDb6+5dUVQ7GTilNXhYkFf9Rq/SKMXXRdl7diSrv/Fzx0JHxTo6vPO7D3FlwLo2rkC +gYAs9bK9hf+xZckNa2QMvYvj40umB+oa0c801TzhUjJbnKeOqDlUdLk4QqZpUHhjaR7V1XA43YUF +R76XNgmWRYMNhGvXUP/bmRNo0YhAabCM/Mq4bntBVXbnZcC1pGpZ+emnYPSnX9U9dv6spfuIC4Yq +a3RFwGaViQbaervfMXArOQKBgFVsZoK6Sp0/lpya22W2tiMwpSw2WSeWhxs6GjKD5qYIcCSkij5q +em9w+tXcqhZvwwWuu+lknh5HDR+LkisJJ/9dzGwpd6xG4g1NKPpg5hzGKamMzp8I0tbSPAv+ASx/ +9JCOSLFwrNNlfc8oE3Sd3b9CE78DAkp35bSff1e7P2p5AoGBAI06vJ552msRVkr1uSgdud1dFDeJ +su0EaD2aGArSq7arnrzvzHL2rHiWTy/qnMu5jpBdrF/6Kvv5SKh3KRIpCMiHkcjDxdvUdfIxZFzD +OOCAf8HSxe19MeCiejHFJDnIMO3KX+vwCyz5lWlu7qFdWlaAg33JnYhYhKfKiQ4bKj3v +-----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 1cc1994..79bb41f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,21 +1,23 @@ +addons: + ssh_known_hosts: + - demo.scouterapm.com dist: xenial language: node_js node_js: - "stable" before_install: + - echo -e "Host $DEMO_SERVER_NODE\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config + - openssl aes-256-cbc -K $encrypted_d662ab6492f2_key -iv $encrypted_d662ab6492f2_iv -in .deploy.enc -out $DEMO_DELOY_FILE -d + - chmod 600 $DEMO_DELOY_FILE - npm install script : - npm run-script build after_success: - - tar cvf $SCOUTER_PAPER-COMPRESS-NAME build/ - - apt-get install sshpass - - apt-get install openssh-client - - echo "$DEMO_DEPLOY_KEY" | tr -d '\r' > $DEMO_DELOY_FILE - - echo "$DEMO_DEPLOY_KEY" | tr -d '\r' > ~/.ssh/id_rsa - - cat ~/.ssh/id_rsa - - ssh-keyscan -H $DEMO_SERVER_NODE >> ~/.ssh/known_hosts - - scp -i $DEMO_DELOY_FILE ./$SCOUTER_PAPER-COMPRESS-NAME $DEMO_SERVER_USER@$DEMO_SERVER_NODE - - ssh -i $DEMO_DELOY_FILE -T $DEMO_SERVER_USER@$DEMO_SERVER_NODE "/home/ec2-user/deploy.sh $SCOUTER_PAPER-COMPRESS-NAME" + - tar cvf $SCOUTER_PAPER_COMPRESS_NAME build/ + - cat $DEMO_DELOY_FILE + - echo "scp -i $DEMO_DELOY_FILE $SCOUTER_PAPER_COMPRESS_NAME $DEMO_SERVER_USER@$DEMO_SERVER_NODE:/home/$DEMO_SERVER_USER" + - scp -i $DEMO_DELOY_FILE $SCOUTER_PAPER_COMPRESS_NAME $DEMO_SERVER_USER@$DEMO_SERVER_NODE:/home/$DEMO_SERVER_USER + - ssh -i $DEMO_DELOY_FILE -T $DEMO_SERVER_USER@$DEMO_SERVER_NODE "/home/ec2-user/deploy.sh $SCOUTER_PAPER_COMPRESS_NAME" cache: directories: - "node_modules" @@ -27,4 +29,5 @@ notifications: slack: scouterapm:fJndRYgbQA0uTLXiEqxZJhUU #: construction_worker: -# job life cycle explain : https://docs.travis-ci.com/user/job-lifecycle/ \ No newline at end of file +# job life cycle explain : https://docs.travis-ci.com/user/job-lifecycle/ +# https://docs.travis-ci.com/user/encrypting-files \ No newline at end of file From 134a5de7201fd8feadf0314cc5a294449bd487dc Mon Sep 17 00:00:00 2001 From: kranian Date: Wed, 26 Jun 2019 19:09:37 +0900 Subject: [PATCH 16/77] :construction_worker: travis ci action deploy #1 --- .deploy.enc | Bin 1671 -> 1680 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/.deploy.enc b/.deploy.enc index 1e13027b953eaf57b4e237a7edfb3d2417e18259..dab1078962b55f9f292b497fc0f3116f6c1fafa4 100644 GIT binary patch literal 1680 zcmV;B250ysF5rvjHgB!7SzvyH*~s`InrK6bEIj_bBSdoxb0L-J;sGhh67%<{lH7B z3uOZQ!5M5}Nhg}(e4t{kr-{^0AUPB?k%cKQ-uq5t%?vu?qN{FGAOw$!3e;LFW_QoY^c%W!8~>|vkr|{n?sZX&ANKv{ zL{jAg5PPf7gP}ADt1V&=;;ZXWRyfV9bnBbww)bVj;hER25;!Z^esc%Ik0x zV2Mc7O-AcMO|37t%BR?y7}=B{&WX4&Q_^j)ZzKh&0Wel9S4nj8chTs?;rRYycr1T+ zP1)~^LncI&<-2GED_)oK217&Wbqrar71ffALSG}NVu~IoLRM28Uudxf0~#8nwBFVz zB8fP+66ed!1eS{ZKyN(VpRp@sQS)oef&B`eK z#5APBRG5LiC3k%!!pr-*WdBbP zujg{4zkKfh(c!>RKrJb&Gf*pm_$=8erxPybSx{Jr)}Aq8kklt2IPsV3n)D1bZOIPigi>TIY2KW#*SUu-9V*! zB^)E-KwucP(n_FG1e*+SUS*JU7C2lsAaT%B-8`DFL2ZwA30s`W89-nR!> zuGMTG3RYAZD}@bsDh6e_;3A+54C5J`0X7+C*5e{oGI%C^PgGetG)5eRK^2PcD6@w` zaEMvgT@~PO6piL9z0T(<@wlK_cAH@OEm<(X4zJ~8KMAY8J-Cu$zvSgG5Tsl$qfnBs zy{|b?=*9A=0;bp(Ie!G!p%4$gL;0Z=XD zXN_-so)SJCXzA$_P3J;OVMbLtFJYsp>bRy15<%GBoQ2;XdptbX0zY;W$UJMm% zIsPMxOswL9C`XAbVAzwOK*dJR`v7RRgR2h_ z-ishPaY_Zkuw7=4>Y08DR(d(@d90aAx6rcv{<4FAMck6(ylAx6*nn7RdfV529s8?V zVh$gX2iC3uvNt))b%yc#!9TxzZnJy3;46Q;4&fqya1&V%;3_zpH>JB>zTr}kzqegF zIPJdRdz(J&{)Z5y9Nx8!zkB7qI_Mv2A6~Ulrk#i7l4ypc?-{l^f@jgYJYIoENHhooH=HP|<{#Gs ze{5F`fiM2i#pK_`U&K&VaXM5E0G$Vp2pIP}u6HpYS{nfA?6Afk)bPWgbjBat_ldm> zu3HigXiCRw;mEYux|h|?YYBEwpZ(L?*t~N%T&tH}fY~KFssJ3of$0ZZn)bfrL7C4ElFS_F@9aFE zP6^$;E6JyeK0QG`XwfpIe4#v?m|9;7v-ygJbTXeXWtlhgY~IWH;D*We;cjWCndb-! zV}HL`;bUlv_ZjxQgu2kX(uRS)AnJ@fG&jIrgWNGoTh343aB=AihQPrbQ>n!3j8b!r zTEOWJB8D;=`;S;d7Z`CzmlGql_BUvePVi-KE3)*st%--_3auv>z3Mn3rRTlx9}CCsnh3ZW|RA5Udj0!(y^E@@s3QY2{U!oj|ONq{@45IL5w+zVDqM7~Z$fMR3=2KeHHOl!3{ z@~4e@ALJ+MM4<8p2BE~KJ?MGyt69pCDM}bq0zKm~EA@*+l#`y)=B~QbC}cEB z6+!4QvfmD7+$6nJvF?I-x)H~2F)&5ztq_y06W5f}pJ-@XjBt~N<2jLQ&m(mLK5VA5 zO@*GXyf;(QF<=6_JHEd;bV~93ffN4K1f7*6+wo46~789N1X)cFhS-8jxl`xs2tWjT;xn+4#zNVwUF@TE54`_QseE*aFg@ zx!P6!o-9aubEQL0!`8{ta)p%)Vc6kn+tQ=oaTo^Mc0_?K?X3V?!yz??e0cqiGmhT=Fp&^eNwK!qn}# zrkYFE)3KhJ5y4jtOKcgVkG@bRE|VUdogFSvB&%*fJVB z)y-V7N*Jv}NO^j5(_MAujaKRNPB;q~6qS0uq)R|7iwD(1@y?Q9jgoK6PLVWWR1NE< s$0$7b??nyKurrHPL_-Sd+f5sjCTa9aZ=HLlcQ=Uq*C(PW{BO_v1J}$W6#xJL From bbef6af79411a4d2fad1091e4b74e135b83515a5 Mon Sep 17 00:00:00 2001 From: kranian Date: Wed, 26 Jun 2019 19:40:54 +0900 Subject: [PATCH 17/77] :construction_worker: travis ci action deploy #2 --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 79bb41f..d2fef50 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,6 @@ script : - npm run-script build after_success: - tar cvf $SCOUTER_PAPER_COMPRESS_NAME build/ - - cat $DEMO_DELOY_FILE - echo "scp -i $DEMO_DELOY_FILE $SCOUTER_PAPER_COMPRESS_NAME $DEMO_SERVER_USER@$DEMO_SERVER_NODE:/home/$DEMO_SERVER_USER" - scp -i $DEMO_DELOY_FILE $SCOUTER_PAPER_COMPRESS_NAME $DEMO_SERVER_USER@$DEMO_SERVER_NODE:/home/$DEMO_SERVER_USER - ssh -i $DEMO_DELOY_FILE -T $DEMO_SERVER_USER@$DEMO_SERVER_NODE "/home/ec2-user/deploy.sh $SCOUTER_PAPER_COMPRESS_NAME" From 00dde681e906f1af9aae8908d07cb432cd704216 Mon Sep 17 00:00:00 2001 From: david100gom Date: Thu, 27 Jun 2019 19:25:13 +0900 Subject: [PATCH 18/77] XLog draw revert --- src/components/Paper/XLog/XLog.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/Paper/XLog/XLog.js b/src/components/Paper/XLog/XLog.js index 99fdb89..67128a5 100644 --- a/src/components/Paper/XLog/XLog.js +++ b/src/components/Paper/XLog/XLog.js @@ -142,11 +142,9 @@ class XLog extends Component { this.redraw(this.props.filter); } else if (this.lastPastTimestamp === this.props.pastTimestamp && this.lastPageCnt !== this.props.pageCnt) { this.lastPageCnt = this.props.pageCnt; - this.clear(); this.draw(this.props.data.newXLogs, this.props.filter); } else { - this.clear(); - this.redraw(this.props.filter); + this.draw(this.props.data.newXLogs, this.props.filter); } if (this.props.filter && JSON.stringify(prevProps.filter) !== JSON.stringify(this.props.filter)) { From 463f9d229c728dc283a751dbf88c3a3fc5697ce8 Mon Sep 17 00:00:00 2001 From: kranian Date: Thu, 27 Jun 2019 19:43:54 +0900 Subject: [PATCH 19/77] :construction_worker: travis ci action development --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d2fef50..019f50a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,6 @@ cache: branches: only: - development - - master notifications: slack: scouterapm:fJndRYgbQA0uTLXiEqxZJhUU From 612b74241022b0dc66b95ab190cd2b0c991ce7ef Mon Sep 17 00:00:00 2001 From: david100gom Date: Fri, 28 Jun 2019 15:17:52 +0900 Subject: [PATCH 20/77] expression refactoring --- src/components/Paper/XLog/Profiler/Profiler.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/Paper/XLog/Profiler/Profiler.js b/src/components/Paper/XLog/Profiler/Profiler.js index 125a413..3ce8288 100644 --- a/src/components/Paper/XLog/Profiler/Profiler.js +++ b/src/components/Paper/XLog/Profiler/Profiler.js @@ -302,8 +302,7 @@ class Profiler extends Component { } // remove duplication txid - var arrTxid = (filtered.map(x => x.txid).toString()).split(","); - var strTxid = arrTxid.reduce(function(a,b){ if(a.indexOf(b) < 0) a.push(b); return a;},[]); + let strTxid = filtered.map(x => x.txid).reduce(function(a,b){ if(a.indexOf(b) < 0) a.push(b); return a;},[]); let date = moment(new Date(x1)).format("YYYYMMDD"); From 888323b4db9b4968db4e8780dc6f27716ac1d748 Mon Sep 17 00:00:00 2001 From: david100gom Date: Fri, 28 Jun 2019 15:41:27 +0900 Subject: [PATCH 21/77] modify setTimeout time from 300 to 100 --- src/components/Paper/XLog/XLog.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Paper/XLog/XLog.js b/src/components/Paper/XLog/XLog.js index 3b897f5..c8ea993 100644 --- a/src/components/Paper/XLog/XLog.js +++ b/src/components/Paper/XLog/XLog.js @@ -144,7 +144,7 @@ class XLog extends Component { this.lastPageCnt = this.props.pageCnt; this.draw(this.props.data.newXLogs, this.props.filter); } else { - this.redraw(this.props.filter); + this.draw(this.props.data.newXLogs, this.props.filter); } if (this.props.filter && JSON.stringify(prevProps.filter) !== JSON.stringify(this.props.filter)) { @@ -196,7 +196,7 @@ class XLog extends Component { this.graphInit(); } } - }, 300); + }, 100); }; From 4cac4ce14498db17f39deae1718b8f676a8b049e Mon Sep 17 00:00:00 2001 From: david100gom Date: Sun, 30 Jun 2019 14:46:21 +0900 Subject: [PATCH 22/77] add loading bar setTimeout --- src/components/Paper/XLog/Profiler/Profiler.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/components/Paper/XLog/Profiler/Profiler.js b/src/components/Paper/XLog/Profiler/Profiler.js index 3ce8288..52496d9 100644 --- a/src/components/Paper/XLog/Profiler/Profiler.js +++ b/src/components/Paper/XLog/Profiler/Profiler.js @@ -354,7 +354,9 @@ class Profiler extends Component { }).fail((xhr, textStatus, errorThrown) => { errorHandler(xhr, textStatus, errorThrown, this.props, "getListData", true); }).always(() => { - this.props.setControlVisibility("Loading", false); + setTimeout(() => { + this.props.setControlVisibility("Loading", false); + }, 300); }); }; @@ -466,7 +468,9 @@ class Profiler extends Component { }).fail((xhr, textStatus, errorThrown) => { errorHandler(xhr, textStatus, errorThrown, this.props, "rowClick_1", true); }).always(() => { - this.props.setControlVisibility("Loading", false); + setTimeout(() => { + this.props.setControlVisibility("Loading", false); + }, 100); }); } @@ -474,7 +478,9 @@ class Profiler extends Component { errorHandler(xhr, textStatus, errorThrown, this.props, "rowClick_2", true); this.props.setControlVisibility("Loading", false); }).always(() => { - this.props.setControlVisibility("Loading", false); + setTimeout(() => { + this.props.setControlVisibility("Loading", false); + }, 100); }); }; From f3ea764911d455920a4459b87012fdbfa0df477a Mon Sep 17 00:00:00 2001 From: david100gom Date: Sun, 4 Aug 2019 11:20:31 +0900 Subject: [PATCH 23/77] add disk usage general paper feature --- src/components/Box/Box.js | 2 + src/components/Paper/DiskUsage/DiskUsage.css | 52 +++++++ src/components/Paper/DiskUsage/DiskUsage.js | 153 +++++++++++++++++++ src/components/Paper/Paper.js | 75 ++++++++- src/components/Paper/PaperControl/Options.js | 9 ++ src/components/index.js | 2 + 6 files changed, 290 insertions(+), 3 deletions(-) create mode 100644 src/components/Paper/DiskUsage/DiskUsage.css create mode 100644 src/components/Paper/DiskUsage/DiskUsage.js diff --git a/src/components/Box/Box.js b/src/components/Box/Box.js index 728f3c9..6d9f912 100644 --- a/src/components/Box/Box.js +++ b/src/components/Box/Box.js @@ -4,6 +4,7 @@ import {Droppable} from 'react-drag-and-drop' import {EmptyBox, ClockBox, XLogBar} from "../../components"; import XLog from "../Paper/XLog/XLog"; import Visitor from "../Paper/Visitor/Visitor"; +import DiskUsage from "../Paper/DiskUsage/DiskUsage"; import LineChart from "../Paper/LineChart/LineChart"; import ActiveSpeed from "../Paper/ActiveSpeed/ActiveSpeed"; import Tooltip from "./Tooltip/Tooltip"; @@ -191,6 +192,7 @@ class Box extends Component { {type === "xlogBar" && } {type === "xlog" && } {type === "visitor" && } + {type === "diskUsage" && } {type === "counter" && } {type === "ActiveSpeed" && } {this.state.tooltip.show && } diff --git a/src/components/Paper/DiskUsage/DiskUsage.css b/src/components/Paper/DiskUsage/DiskUsage.css new file mode 100644 index 0000000..939e3a0 --- /dev/null +++ b/src/components/Paper/DiskUsage/DiskUsage.css @@ -0,0 +1,52 @@ +.disk-usage-list { + width: 100%; + display: table; + text-align: left; + font-size: 1px; +} + +.disk-usage-list > div.row { + display: table-row; + cursor: pointer; +} + +.disk-usage-list > div.row.active { + background-color: #26466d; + color: white; + font-size: 10px; +} + +.disk-usage-list > div:hover { + background-color: rgba(0, 0, 0, 0.1); +} + +.disk-usage-list > div.header.row { + background-color: #F2F2F2; +} + +.disk-usage-list > div:first-child > span { + border-top: 1px solid #CCC; +} + +.disk-usage-list > div.header:first-child > span { + border-top: none; +} + +.disk-usage-list > div.header.row.fixed > span { + position: sticky; + top: 0; + background-color: #F2F2F2; +} + +.disk-usage-list > div > span { + display: table-cell; + line-height: 100%; + vertical-align: middle; + white-space: nowrap; + padding: 10px; + border-bottom: 1px solid #CCC; +} + +.disk-usage-list > div.header > span { + border-bottom: none; +} diff --git a/src/components/Paper/DiskUsage/DiskUsage.js b/src/components/Paper/DiskUsage/DiskUsage.js new file mode 100644 index 0000000..ebb8136 --- /dev/null +++ b/src/components/Paper/DiskUsage/DiskUsage.js @@ -0,0 +1,153 @@ +import React, {Component} from 'react'; +import './DiskUsage.css'; + +const layout = [ + { + key: "objName", + name: "OBJECT" + }, + { + key: "device", + name: "DEVICE" + }, + { + key: "diskMount", + name: "MOUNT" + }, + { + key: "diskUsed", + name: "USED", + type: "bytes" + }, + { + key: "diskTotal", + name: "TOTAL", + type: "bytes" + }, + { + key: "diskUsage", + name: "USAGE", + type: "percent" + } +]; + +class DiskUsage extends Component { + + constructor(props) { + super(props); + this.state = { + allInstance: null, + boxHeight: 150, + boxWidth : 499 + }; + } + + componentWillReceiveProps() { + + let allDisk = this.props.diskUsage; + + if (!allDisk || !allDisk.time || !allDisk.diskUsage) { + return; + } + + let allInstance = []; + let arrDiskUsage = allDisk.diskUsage; + let arrObjName = allDisk.objName; + + let box = this.refs.listBox.parentNode.parentNode.parentNode.parentNode; + + arrDiskUsage.map((data, index) => { + + if(data !== null) { + JSON.stringify((data).map((instance) => { + if(instance.used > 0 && instance.mount) { + instance.objName = arrObjName[index]; + instance.diskUsage = Math.round((instance.used / instance.total) * 100) + instance.diskUsed = instance.used; + instance.diskTotal = instance.total; + instance.diskMount = instance.mount; + allInstance.push(instance); + } + return null; + })); + } + return null; + }); + + if(allInstance.length > 0) { + this.setState({ + allInstance : allInstance, + boxHeight : box.offsetHeight, + boxWidth : box.offsetWidth + }); + } + + } + + getRow = (row, i) => { + + let limit = this.state.boxHeight/40; + if(this.state.boxHeight < 151) limit = 3; + if(this.state.boxHeight < 121) limit = 1; + + return (i < limit) && layout.map((meta, j) => { + + let limit = this.state.boxWidth/100; + if(this.state.boxWidth < 250) limit = 0; + + let className = meta.key; + let data = row[className]; + + if(data !== undefined && (j < limit)){ + if (meta.type === "bytes") { + return {this.bytesToSize(data)} + }else if (meta.type === "percent"){ + return {data}% + }else{ + return {data} + } + } + return null; + }); + }; + + getHeader = () => { + return layout.map((meta, j) => { + + let limit = this.state.boxWidth/100; + + if(this.state.boxWidth < 250) limit = 0; + + if(j < limit){ + if(meta.key === 'objName') { + return {meta.name} (Total : {this.state.allInstance.length}) + }else{ + return {meta.name} + } + } + return null; + }); + }; + + bytesToSize(bytes) { + const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; + if (bytes === 0) return 'n/a'; + const i = parseInt(Math.floor(Math.log(Math.abs(bytes)) / Math.log(1024)), 10); + if (i === 0) return `${bytes} ${sizes[i]})`; + return `${(bytes / (1024 ** i)).toFixed(1)} ${sizes[i]}`; + } + + render() { + return ( +
+
{this.state.allInstance && this.getHeader()}
+ {this.state.allInstance && this.state.allInstance.map((data, i) => { + return
{this.getRow(data, i)}
; + })} +
+ ); + } + +} + +export default DiskUsage; \ No newline at end of file diff --git a/src/components/Paper/Paper.js b/src/components/Paper/Paper.js index d325f3f..b28848a 100644 --- a/src/components/Paper/Paper.js +++ b/src/components/Paper/Paper.js @@ -211,6 +211,9 @@ class Paper extends Component { /* visitor */ visitor: {}, + /* diskUsage */ + diskUsage: {}, + /* counters */ counters: { time: null, @@ -469,6 +472,7 @@ class Paper extends Component { } this.getVisitor(this.props); + this.getDiskUsage(this.props); this.getRealTimeCounter(); clearInterval(this.dataRefreshTimer); @@ -1050,6 +1054,71 @@ class Paper extends Component { } }; + getDiskUsage = (props) => { + let that = this; + if (props.objects && props.objects.length > 0) { + let filterdObjects = props.objects.filter((instance) => { + return props.filterMap[instance.objHash] + }); + + if (filterdObjects.length > 0) { + + props.addRequest(); + + let arrTime = []; + let arrDiskUsage = []; + let arrObjName = []; + + filterdObjects.map((data) => { + jQuery.ajax({ + method: "GET", + async: true, + url: getHttpProtocol(props.config) + '/scouter/v1/object/host/realTime/disk/ofObject/'+ JSON.parse(JSON.stringify(data)).objHash, + xhrFields: getWithCredentials(props.config), + beforeSend: function (xhr) { + setAuthHeader(xhr, props.config, getCurrentUser(props.config, props.user)); + } + }).done((msg) => { + if (!that.mounted) { + return null; + } + + let time = (new ServerDate()).getTime(); + arrTime.push(time); + arrDiskUsage.push(msg.result); + arrObjName.push(JSON.parse(JSON.stringify(data)).objName) + + }).fail((xhr, textStatus, errorThrown) => { + }); + + return null; + }); + + setTimeout(() =>{ + if(arrTime.length > 0) { + this.setState({ + diskUsage: { + time: arrTime, + objName: arrObjName, + diskUsage: arrDiskUsage + } + }); + } + + },3000); + + } else { + let time = (new ServerDate()).getTime(); + this.setState({ + diskUsage: { + time: time, + objName : [], + diskUsage: [] + } + }); + } + } + }; getCounterHistoryData = (url, counterKey, from, to, now, append) => { this.setLoading(true); @@ -1531,12 +1600,12 @@ class Paper extends Component { return (
- {box.option && } - {box.option && box.option.type !== "xlog" && box.option && } + {box.option && box.option.type !== 'diskUsage' && } + {box.option && box.option.mode !== "exclusive" && box.option && } {box.option && (box.option.length > 1 || box.option.config ) && box.option.type === "xlog" && } {box.config && } {filterInfo && filterInfo.show && } - this.boxesRef[box.key] = ref} visible={this.state.visible} setOption={this.setOption} box={box} filter={filterInfo ? filterInfo.data : {filtering: false}} pastTimestamp={this.state.pastTimestamp} pageCnt={this.state.pageCnt} data={this.state.data} config={this.props.config} visitor={this.state.visitor} counters={this.state.counters} countersHistory={this.state.countersHistory.data} countersHistoryFrom={this.state.countersHistory.from} countersHistoryTo={this.state.countersHistory.to} countersHistoryTimestamp={this.state.countersHistory.time} longTerm={this.props.range.longTerm} layoutChangeTime={this.props.layoutChangeTime} realtime={this.props.range.realTime} xlogHistoryDoing={this.state.xlogHistoryDoing} xlogHistoryRequestCnt={this.state.xlogHistoryRequestCnt} setStopXlogHistory={this.setStopXlogHistory} xlogNotSupportedInRange={this.state.xlogNotSupportedInRange}/> + this.boxesRef[box.key] = ref} visible={this.state.visible} setOption={this.setOption} box={box} filter={filterInfo ? filterInfo.data : {filtering: false}} pastTimestamp={this.state.pastTimestamp} pageCnt={this.state.pageCnt} data={this.state.data} config={this.props.config} visitor={this.state.visitor} diskUsage={this.state.diskUsage} counters={this.state.counters} countersHistory={this.state.countersHistory.data} countersHistoryFrom={this.state.countersHistory.from} countersHistoryTo={this.state.countersHistory.to} countersHistoryTimestamp={this.state.countersHistory.time} longTerm={this.props.range.longTerm} layoutChangeTime={this.props.layoutChangeTime} realtime={this.props.range.realTime} xlogHistoryDoing={this.state.xlogHistoryDoing} xlogHistoryRequestCnt={this.state.xlogHistoryRequestCnt} setStopXlogHistory={this.setStopXlogHistory} xlogNotSupportedInRange={this.state.xlogNotSupportedInRange}/>
) })} diff --git a/src/components/Paper/PaperControl/Options.js b/src/components/Paper/PaperControl/Options.js index edc68f3..f07fd38 100644 --- a/src/components/Paper/PaperControl/Options.js +++ b/src/components/Paper/PaperControl/Options.js @@ -90,6 +90,15 @@ export function options() { } } }, + diskUsage: { + icon: "fa-hdd-o", + mode: "exclusive", + type: "diskUsage", + title: "DISK USAGE", + config: { + + } + }, activeSpeed: { icon: "fa-bars", mode: "exclusive", diff --git a/src/components/index.js b/src/components/index.js index 6c23f1f..bfd718a 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -9,6 +9,7 @@ import Box from './Box/Box'; import Paper from './Paper/Paper'; import BoxConfig from './Paper/BoxConfig/BoxConfig'; import ClockBox from './Paper/ClockBox/ClockBox'; +import DiskUsage from './Paper/DiskUsage/DiskUsage'; import XLogBar from './Paper/XLogBar/XLogBar'; import EmptyBox from './Paper/EmptyBox/EmptyBox'; import PaperControl from './Paper/PaperControl/PaperControl'; @@ -31,6 +32,7 @@ export { Paper, BoxConfig, ClockBox, + DiskUsage, XLogBar, EmptyBox, PaperControl, From f42f6856719517006c0497c80e6efc25979d5162 Mon Sep 17 00:00:00 2001 From: david100gom Date: Mon, 5 Aug 2019 18:18:08 +0900 Subject: [PATCH 24/77] update disk usage general paper feature --- src/components/Paper/DiskUsage/DiskUsage.css | 74 +++++++++++++------- src/components/Paper/DiskUsage/DiskUsage.js | 30 ++++---- src/components/Paper/Paper.js | 64 +++++++++-------- 3 files changed, 100 insertions(+), 68 deletions(-) diff --git a/src/components/Paper/DiskUsage/DiskUsage.css b/src/components/Paper/DiskUsage/DiskUsage.css index 939e3a0..b693a3c 100644 --- a/src/components/Paper/DiskUsage/DiskUsage.css +++ b/src/components/Paper/DiskUsage/DiskUsage.css @@ -1,52 +1,76 @@ .disk-usage-list { - width: 100%; - display: table; text-align: left; - font-size: 1px; + overflow-y: scroll; + height: 100%; } .disk-usage-list > div.row { - display: table-row; - cursor: pointer; + display: table; + text-align: left; + font-size: 1px; + width: 100%; } -.disk-usage-list > div.row.active { +.disk-usage-list > div.row.table-title { + display: table; background-color: #26466d; color: white; + text-align: left; font-size: 10px; } -.disk-usage-list > div:hover { - background-color: rgba(0, 0, 0, 0.1); -} - .disk-usage-list > div.header.row { background-color: #F2F2F2; } -.disk-usage-list > div:first-child > span { - border-top: 1px solid #CCC; -} - -.disk-usage-list > div.header:first-child > span { - border-top: none; -} - -.disk-usage-list > div.header.row.fixed > span { - position: sticky; - top: 0; - background-color: #F2F2F2; -} - .disk-usage-list > div > span { display: table-cell; + text-align: left; line-height: 100%; vertical-align: middle; - white-space: nowrap; + white-space: pre-wrap; padding: 10px; border-bottom: 1px solid #CCC; } +.disk-usage-list > div > span.objName { + width: 30%; +} +.disk-usage-list > div > span.diskDevice { + width: 15%; +} + +.disk-usage-list > div > span.diskMount { + width: 15%; +} + +.disk-usage-list > div > span.diskUsed { + width: 15%; +} + +.disk-usage-list > div > span.diskTotal { + width: 15%; +} + +.disk-usage-list > div > span.diskUsage { + width: 10%; +} + .disk-usage-list > div.header > span { border-bottom: none; } + +.disk-usage-list .no-data { + text-align: center; + height: 100%; + display: table; + width: 100%; + font-size: 10px; +} + +.disk-usage-list .no-data > div { + display: table-cell; + width: 100%; + height: 100%; + vertical-align: middle; +} \ No newline at end of file diff --git a/src/components/Paper/DiskUsage/DiskUsage.js b/src/components/Paper/DiskUsage/DiskUsage.js index ebb8136..460b3fa 100644 --- a/src/components/Paper/DiskUsage/DiskUsage.js +++ b/src/components/Paper/DiskUsage/DiskUsage.js @@ -7,7 +7,7 @@ const layout = [ name: "OBJECT" }, { - key: "device", + key: "diskDevice", name: "DEVICE" }, { @@ -56,16 +56,18 @@ class DiskUsage extends Component { let box = this.refs.listBox.parentNode.parentNode.parentNode.parentNode; + // used & total 사이즈가 0 보다 큰 데이터만 정리 arrDiskUsage.map((data, index) => { if(data !== null) { JSON.stringify((data).map((instance) => { - if(instance.used > 0 && instance.mount) { + if(instance.used > 0 && instance.total > 0) { instance.objName = arrObjName[index]; instance.diskUsage = Math.round((instance.used / instance.total) * 100) instance.diskUsed = instance.used; instance.diskTotal = instance.total; instance.diskMount = instance.mount; + instance.diskDevice = instance.device; allInstance.push(instance); } return null; @@ -86,11 +88,7 @@ class DiskUsage extends Component { getRow = (row, i) => { - let limit = this.state.boxHeight/40; - if(this.state.boxHeight < 151) limit = 3; - if(this.state.boxHeight < 121) limit = 1; - - return (i < limit) && layout.map((meta, j) => { + return layout.map((meta, j) => { let limit = this.state.boxWidth/100; if(this.state.boxWidth < 250) limit = 0; @@ -100,11 +98,11 @@ class DiskUsage extends Component { if(data !== undefined && (j < limit)){ if (meta.type === "bytes") { - return {this.bytesToSize(data)} + return {this.bytesToSize(data)} }else if (meta.type === "percent"){ - return {data}% + return {data}% }else{ - return {data} + return {data} } } return null; @@ -115,20 +113,20 @@ class DiskUsage extends Component { return layout.map((meta, j) => { let limit = this.state.boxWidth/100; - if(this.state.boxWidth < 250) limit = 0; if(j < limit){ if(meta.key === 'objName') { - return {meta.name} (Total : {this.state.allInstance.length}) + return {meta.name} (Total : {this.state.allInstance.length}) }else{ - return {meta.name} + return {meta.name} } } return null; }); }; + // 데이터 변환 bytesToSize(bytes) { const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; if (bytes === 0) return 'n/a'; @@ -139,11 +137,13 @@ class DiskUsage extends Component { render() { return ( -
-
{this.state.allInstance && this.getHeader()}
+
+
{this.state.allInstance && this.getHeader()}
{this.state.allInstance && this.state.allInstance.map((data, i) => { return
{this.getRow(data, i)}
; })} + {(!this.state.allInstance) &&
+
NO DATA RECEIVED
}
); } diff --git a/src/components/Paper/Paper.js b/src/components/Paper/Paper.js index b28848a..abb7a1a 100644 --- a/src/components/Paper/Paper.js +++ b/src/components/Paper/Paper.js @@ -1056,9 +1056,20 @@ class Paper extends Component { getDiskUsage = (props) => { let that = this; - if (props.objects && props.objects.length > 0) { + let time = (new ServerDate()).getTime(); + let refreshTime = 1000 * 60 * 15; // 15min + let diffTime = time - (!this.state.diskRefreshTime ? time-refreshTime: this.state.diskRefreshTime); + + if (props.objects && props.objects.length > 0 && diffTime >= refreshTime) { + + this.setState({ + diskRefreshTime: time, + }); + let filterdObjects = props.objects.filter((instance) => { return props.filterMap[instance.objHash] + }).filter((data) => { + return JSON.parse(JSON.stringify(data)).objFamily === 'host' }); if (filterdObjects.length > 0) { @@ -1069,10 +1080,11 @@ class Paper extends Component { let arrDiskUsage = []; let arrObjName = []; - filterdObjects.map((data) => { + filterdObjects.forEach(function (data) { + jQuery.ajax({ method: "GET", - async: true, + async: false, url: getHttpProtocol(props.config) + '/scouter/v1/object/host/realTime/disk/ofObject/'+ JSON.parse(JSON.stringify(data)).objHash, xhrFields: getWithCredentials(props.config), beforeSend: function (xhr) { @@ -1091,31 +1103,18 @@ class Paper extends Component { }).fail((xhr, textStatus, errorThrown) => { }); - return null; }); - setTimeout(() =>{ - if(arrTime.length > 0) { - this.setState({ - diskUsage: { - time: arrTime, - objName: arrObjName, - diskUsage: arrDiskUsage - } - }); - } - - },3000); - - } else { - let time = (new ServerDate()).getTime(); - this.setState({ - diskUsage: { - time: time, - objName : [], - diskUsage: [] - } - }); + if(arrTime.length > 0) { + arrObjName.sort(); + this.setState({ + diskUsage: { + time: arrTime, + objName: arrObjName, + diskUsage: arrDiskUsage + } + }); + } } } }; @@ -1310,7 +1309,16 @@ class Paper extends Component { let boxes = this.props.boxes; boxes.forEach((box, i) => { if (box.key === key) { - this.getSingleCounterHistory(box); + + if(box.option !== undefined && box.option.type !== undefined && box.option.type === "diskUsage") { + this.setLoading(true); + this.setState({ + diskRefreshTime: null + }); + setTimeout(() =>{ this.setLoading(false); },300); + }else{ + this.getSingleCounterHistory(box); + } return false; } }); @@ -1601,7 +1609,7 @@ class Paper extends Component {
{box.option && box.option.type !== 'diskUsage' && } - {box.option && box.option.mode !== "exclusive" && box.option && } + {box.option && box.option.type !== "xlog" && box.option && } {box.option && (box.option.length > 1 || box.option.config ) && box.option.type === "xlog" && } {box.config && } {filterInfo && filterInfo.show && } From 0ef27390aa967815c80076d55fbd343a35a4745d Mon Sep 17 00:00:00 2001 From: david100gom Date: Thu, 8 Aug 2019 15:37:48 +0900 Subject: [PATCH 25/77] update disk usage general paper feature --- src/components/Paper/DiskUsage/DiskUsage.css | 6 +---- src/components/Paper/DiskUsage/DiskUsage.js | 23 ++++++-------------- src/components/Paper/Paper.js | 4 ++-- src/components/Paper/PaperControl/Options.js | 2 +- 4 files changed, 11 insertions(+), 24 deletions(-) diff --git a/src/components/Paper/DiskUsage/DiskUsage.css b/src/components/Paper/DiskUsage/DiskUsage.css index b693a3c..8e2400b 100644 --- a/src/components/Paper/DiskUsage/DiskUsage.css +++ b/src/components/Paper/DiskUsage/DiskUsage.css @@ -1,6 +1,6 @@ .disk-usage-list { text-align: left; - overflow-y: scroll; + overflow-y: auto; height: 100%; } @@ -19,10 +19,6 @@ font-size: 10px; } -.disk-usage-list > div.header.row { - background-color: #F2F2F2; -} - .disk-usage-list > div > span { display: table-cell; text-align: left; diff --git a/src/components/Paper/DiskUsage/DiskUsage.js b/src/components/Paper/DiskUsage/DiskUsage.js index 460b3fa..bed08c6 100644 --- a/src/components/Paper/DiskUsage/DiskUsage.js +++ b/src/components/Paper/DiskUsage/DiskUsage.js @@ -83,20 +83,16 @@ class DiskUsage extends Component { boxWidth : box.offsetWidth }); } - } getRow = (row, i) => { return layout.map((meta, j) => { - let limit = this.state.boxWidth/100; - if(this.state.boxWidth < 250) limit = 0; - let className = meta.key; let data = row[className]; - if(data !== undefined && (j < limit)){ + if(data !== undefined){ if (meta.type === "bytes") { return {this.bytesToSize(data)} }else if (meta.type === "percent"){ @@ -112,17 +108,12 @@ class DiskUsage extends Component { getHeader = () => { return layout.map((meta, j) => { - let limit = this.state.boxWidth/100; - if(this.state.boxWidth < 250) limit = 0; - - if(j < limit){ - if(meta.key === 'objName') { - return {meta.name} (Total : {this.state.allInstance.length}) - }else{ - return {meta.name} - } + if(meta.key === 'objName') { + return {meta.name} (Total : {this.state.allInstance.length}) + }else{ + return {meta.name} } - return null; + }); }; @@ -137,7 +128,7 @@ class DiskUsage extends Component { render() { return ( -
+
{this.state.allInstance && this.getHeader()}
{this.state.allInstance && this.state.allInstance.map((data, i) => { return
{this.getRow(data, i)}
; diff --git a/src/components/Paper/Paper.js b/src/components/Paper/Paper.js index abb7a1a..528b690 100644 --- a/src/components/Paper/Paper.js +++ b/src/components/Paper/Paper.js @@ -1315,7 +1315,7 @@ class Paper extends Component { this.setState({ diskRefreshTime: null }); - setTimeout(() =>{ this.setLoading(false); },300); + setTimeout(() =>{ this.setLoading(false); },100); }else{ this.getSingleCounterHistory(box); } @@ -1609,7 +1609,7 @@ class Paper extends Component {
{box.option && box.option.type !== 'diskUsage' && } - {box.option && box.option.type !== "xlog" && box.option && } + {box.option && box.option.type !== "xlog" && (box.option.mode !== "exclusive" || box.option.type === 'diskUsage') && box.option && } {box.option && (box.option.length > 1 || box.option.config ) && box.option.type === "xlog" && } {box.config && } {filterInfo && filterInfo.show && } diff --git a/src/components/Paper/PaperControl/Options.js b/src/components/Paper/PaperControl/Options.js index f07fd38..5a41350 100644 --- a/src/components/Paper/PaperControl/Options.js +++ b/src/components/Paper/PaperControl/Options.js @@ -94,7 +94,7 @@ export function options() { icon: "fa-hdd-o", mode: "exclusive", type: "diskUsage", - title: "DISK USAGE", + title: "Disk Usage", config: { } From fc9100ea4db212a190962ade529d21ddd96b0988 Mon Sep 17 00:00:00 2001 From: kranian Date: Thu, 10 Oct 2019 09:52:57 +0900 Subject: [PATCH 26/77] :arrow_up: security vulnerabilities dependencies upgrade - axios - eslint - lodash --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index bb0616c..7e2a3de 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "dependencies": { "autoprefixer": "7.1.6", - "axios": "^0.17.1", + "axios": "^0.18.1", "babel-core": "6.26.0", "babel-eslint": "7.2.3", "babel-jest": "20.0.3", @@ -22,7 +22,7 @@ "d3-scale-chromatic": "^1.3.3", "detect-browser": "^2.1.0", "dotenv": "4.0.0", - "eslint": "4.10.0", + "eslint": "^4.18.2", "eslint-config-react-app": "^2.0.1", "eslint-loader": "1.9.0", "eslint-plugin-flowtype": "2.39.1", @@ -36,7 +36,7 @@ "html-webpack-plugin": "2.29.0", "jest": "20.0.4", "jquery": "^3.2.1", - "lodash": "^4.17.11", + "lodash": "^4.17.15", "moment": "^2.23.0", "numeral": "^2.0.6", "object-assign": "4.1.1", From 7467cd8191bc48d9cb3790883371a8e8eefe80a0 Mon Sep 17 00:00:00 2001 From: kranian Date: Thu, 10 Oct 2019 19:25:10 +0900 Subject: [PATCH 27/77] =?UTF-8?q?:construction:=20TX=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20VIEW=20=ED=91=9C=EC=8B=9C=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/idAbbr.js | 2 +- .../Profiler/FrameProfile/FrameProfile.js | 31 ++++++++++++++++--- .../Paper/XLog/Profiler/FrameProfile/meta.js | 4 +-- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/common/idAbbr.js b/src/common/idAbbr.js index ea6169b..0be627f 100644 --- a/src/common/idAbbr.js +++ b/src/common/idAbbr.js @@ -19,7 +19,7 @@ function toString32(num) { const minus = num < 0; if (minus) { - let rad32 = MINUS + (-1 * num).toString(32); + let rad32 = MINUS + (-num).toString(32); return rad32; } else { diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.js b/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.js index 1f4cc59..5a2c93a 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.js @@ -407,13 +407,20 @@ class FrameProfile extends Component { }; + boolTostr = (val="0") => { + switch (val) { + case "1": return "Y"; + case "0": return "N"; + default: + return "N"; + } + }; render() { let nav = null; if (this.props.profile) { nav = this.getNavData(this.props.profile.endTime, this.props.profile.gxid, this.props.profile.caller, this.props.profile.txid, this.props.steps); } - return (
GENERAL INFO
@@ -436,17 +443,31 @@ class FrameProfile extends Component {
} + { + this.props.profile &&
+ txid + {IdAbbr.toString32(this.props.profile.txid)} +
+ } + { + this.props.profile && +
+ gxid + {IdAbbr.toString32(this.props.profile.gxid)} +
+ } {this.props.profile && profileMetas && profileMetas.filter((d) => { - return this.props.summary ? d.show : true + return this.props.summary ? (d.show && this.props.profile[d.key] ) : true }).map((meta, i) => { return
{meta.name} {meta.type === "datetime" && d3.timeFormat(this.fullTimeFormat)(new Date(Number(this.props.profile[meta.key])))} - {meta.type === "ms" && numeral(this.props.profile[meta.key]).format(this.props.config.numberFormat) + " ms"} - {meta.type === "bytes" && numeral(this.props.profile[meta.key]).format(this.props.config.numberFormat + "b")} + {meta.type === "ms" && `${numeral(this.props.profile[meta.key]).format(this.props.config.numberFormat)} ms`} + {meta.type === "bytes" && `${numeral(this.props.profile[meta.key]).format(this.props.config.numberFormat)} b`} {meta.type === "number" && numeral(this.props.profile[meta.key]).format(this.props.config.numberFormat)} - {(meta.type !== "datetime" && meta.type !== "ms" && meta.type !== "bytes" && meta.type !== "number") && this.props.profile[meta.key]} + {meta.type === "boolean" && this.boolTostr(this.props.profile[meta.key]) } + {(meta.type !== "datetime" && meta.type !== "ms" && meta.type !== "bytes" && meta.type !== "number" && meta.type !== "boolean") && this.props.profile[meta.key]}
})} diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/meta.js b/src/components/Paper/XLog/Profiler/FrameProfile/meta.js index 346dea0..1db9abe 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/meta.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/meta.js @@ -118,7 +118,7 @@ export const profileMetas = [ key: "hasDump", name: "Dump", type: "boolean", - show: false + show: true }, { key: "internalId", @@ -211,7 +211,7 @@ export const profileMetas = [ key: "userAgent", name: "User Agent", type: "string", - show: false + show: true }, { key: "xlogType", From c365c3f75b8a5545f4b7e4755730d0689fea7099 Mon Sep 17 00:00:00 2001 From: kranian Date: Fri, 11 Oct 2019 18:07:38 +0900 Subject: [PATCH 28/77] :construction: tx click action adding --- http/serviceMap.json | 45 +++++++++++++++++++ http/tree.json | 27 +++++++++++ http/xflow.http | 40 +++++++++++++++++ .../Profiler/FrameProfile/FrameProfile.css | 10 +++++ .../Profiler/FrameProfile/FrameProfile.js | 28 +++++++----- 5 files changed, 139 insertions(+), 11 deletions(-) create mode 100644 http/serviceMap.json create mode 100644 http/tree.json create mode 100644 http/xflow.http diff --git a/http/serviceMap.json b/http/serviceMap.json new file mode 100644 index 0000000..7d73d02 --- /dev/null +++ b/http/serviceMap.json @@ -0,0 +1,45 @@ +{ + "-2608246922300427269" : { + "name" : "/traceability/cross-service/complex\n(/sc-api-demo-m01.localdomain/sampleweb)", + "error" : "", + "xtype" : "WEB_SERVICE", + "elapsed" : "485", + "threadName" : "http-nio-8080-exec-5", + "tag" : { + "caller" : "-2608246922300427269", + "ip": "172.31.31.84", + "serverId" : "scouter-demo-collector" + } + }, + "-1561743063494029368" : { + "name" : "/traceability/cross-service/simple2\n(/sc-api-demo-m01.localdomain/sampleweb)", + "error" : "", + "xtype" : "WEB_SERVICE", + "threadName" : "http-nio-8080-exec-5", + "tag" : { + "caller" : "-2608246922300427269", + "ip": "172.31.31.84", + "serverId" : "scouter-demo-collector" + }, + "childMap" : { + "210230" : { + "name" :"", + "error" :"", + "xtype" :"", + "threadName" :"" + }, + "210231" : { + "name" :"", + "error" :"", + "xtype" :"", + "address":"", + "threadName" :"", + "tag" :{ + "serverId" : "scouter-demo-collector" + } + } + } + } +} + + diff --git a/http/tree.json b/http/tree.json new file mode 100644 index 0000000..57a5c28 --- /dev/null +++ b/http/tree.json @@ -0,0 +1,27 @@ +{ + "name": "0.0.0.0", + "parent": "null", + "children": [ + { + "name": "/traceability/cross-service/complex", + "parent": "0.0.0.0", + "children": [ + { + "name": "/traceability/cross-service/simple2", + "parent": "/traceability/cross-service/complex" + }, + { + "name": "/traceability/cross-service/simple", + "parent": "/traceability/cross-service/complex" + }, + { + + } + ] + }, + { + "name": "Level 2: B", + "parent": "Top Level" + } + ] +} diff --git a/http/xflow.http b/http/xflow.http new file mode 100644 index 0000000..dd2a4fd --- /dev/null +++ b/http/xflow.http @@ -0,0 +1,40 @@ +GET http://demo.scouterapm.com:6188/scouter/v1/xlog-data/20191010/gxid/-2608246922300427269 +### +GET http://demo.scouterapm.com:6188/scouter/v1/profile-data/20191010/-2608246922300427269 + +### + +#filter -> +# xlogType : + +# public final static byte WEB_SERVICE = 0; +# public final static byte APP_SERVICE = 1; +# public final static byte BACK_THREAD = 2; +# public final static byte ASYNCSERVLET_DISPATCHED_SERVICE = 3; +# public final static byte BACK_THREAD2 = 4; +# public final static byte ZIPKIN_SPAN = 5; +################## +# public final static byte METHOD = 1; +# public final static byte METHOD2 = 10; +# public final static byte SPAN = 51; +# public final static byte SQL = 2; +# public final static byte SQL2 = 8; +# public final static byte SQL3 = 16; +# public final static byte MESSAGE = 3; +# public final static byte SOCKET = 5; +# public final static byte APICALL = 6; +# public final static byte APICALL2 = 15; +# public final static byte SPANCALL = 52; +# public final static byte THREAD_SUBMIT = 7; +# public final static byte HASHED_MESSAGE = 9; +# public final static byte PARAMETERIZED_MESSAGE = 17; +# public final static byte DUMP = 12; +# public final static byte DISPATCH = 13; +# public final static byte THREAD_CALL_POSSIBLE = 14; +# +# public final static byte METHOD_SUM = 11; +# public final static byte SQL_SUM = 21; +# public final static byte MESSAGE_SUM = 31; +# public final static byte SOCKET_SUM = 42; +# public final static byte APICALL_SUM = 43; +# public final static byte CONTROL = 99; \ No newline at end of file diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.css b/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.css index 4aaf25c..f3e488b 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.css +++ b/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.css @@ -53,6 +53,16 @@ color: #FF030D; } +.frame-profile div.xlog-data span.data.tx-link { + cursor:pointer; + color:#7B3E81; + text-decoration:underline; +} +.frame-profile div.xlog-data span.data.tx-link:hover { + text-decoration:none; +} + + .frame-profile div.xlog-data.wrap span.data { white-space: normal; word-break: break-all; diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.js b/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.js index 5a2c93a..df9a03b 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.js @@ -415,6 +415,13 @@ class FrameProfile extends Component { return "N"; } }; + onClickTxToXFlow=(gxid,txid,caller,endtime) =>{ + console.log('onClickTxToXFlow',gxid,txid,caller,endtime); + const yyyymmdd = moment(new Date(Number(endtime))).format("YYYYMMDD"); + const url=`/scouter/v1/xlog-data/${yyyymmdd}/gxid/${gxid}`; + console.log(url); + + }; render() { let nav = null; @@ -444,18 +451,17 @@ class FrameProfile extends Component {
} { - this.props.profile &&
- txid - {IdAbbr.toString32(this.props.profile.txid)} -
- } - { - this.props.profile && -
- gxid - {IdAbbr.toString32(this.props.profile.gxid)} -
+ this.props.profile && ['Txid','Gxid'].map(k=>{ + return
+ {k} + { + const { gxid,txid,caller,endTime } = this.props.profile; + this.onClickTxToXFlow(gxid,txid,caller,endTime); + }}>{IdAbbr.toString32(this.props.profile[k.toLowerCase()])} +
+ }) } + {this.props.profile && profileMetas && profileMetas.filter((d) => { return this.props.summary ? (d.show && this.props.profile[d.key] ) : true }).map((meta, i) => { From b79dd682862534d3e054f009268a43b13bbfd1fd Mon Sep 17 00:00:00 2001 From: "yeosong.heo" Date: Sun, 13 Oct 2019 01:16:01 +0900 Subject: [PATCH 29/77] :construction: tx name abbr --- src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.js b/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.js index df9a03b..9295ac2 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.js @@ -457,7 +457,7 @@ class FrameProfile extends Component { { const { gxid,txid,caller,endTime } = this.props.profile; this.onClickTxToXFlow(gxid,txid,caller,endTime); - }}>{IdAbbr.toString32(this.props.profile[k.toLowerCase()])} + }}>{IdAbbr.abbr(this.props.profile[k.toLowerCase()])}
}) } From 69a872a9a6153eec6eaa2f5cf0fca5958f916cd1 Mon Sep 17 00:00:00 2001 From: kranian Date: Mon, 14 Oct 2019 19:07:40 +0900 Subject: [PATCH 30/77] =?UTF-8?q?:construction:=20tx=20flow=20=EA=B3=B5?= =?UTF-8?q?=EA=B0=84=20frame=20adding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Profiler/FrameProfile/FrameProfile.css | 29 ++++++++++ .../Profiler/FrameProfile/FrameProfile.js | 47 ++++++++++++++-- .../FrameProfile/XlogFlow/XlogFlow.css | 25 +++++++++ .../FrameProfile/XlogFlow/XlogFlow.js | 56 +++++++++++++++++++ .../FrameProfile/XlogFlow/XlogFlow.styl | 20 +++++++ .../XlogFlow/XlogFlowChart/XlogFlowChart.js | 28 ++++++++++ .../XlogFlow/XlogFlowChart/XlogFlowRender.js | 48 ++++++++++++++++ 7 files changed, 248 insertions(+), 5 deletions(-) create mode 100644 src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.css create mode 100644 src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js create mode 100644 src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.styl create mode 100644 src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowChart.js create mode 100644 src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowRender.js diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.css b/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.css index f3e488b..e9aadd0 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.css +++ b/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.css @@ -452,4 +452,33 @@ .frame-profile-step-detail pre { font-family: 'Righteous', 'Nanum Gothic', cursive !important; margin: 0; +} + +.frame-profile .frame-xlog-flow { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.6); + display: table; + width: 100%; + height: 100%; + z-index: 2; +} + +.frame-profile .frame-xlog-flow > div { + display: table-cell; + width: 100%; + height: 100%; + vertical-align: bottom; + padding: 0 10px; + position: relative; + box-sizing: border-box; +} + +.frame-profile .frame-xlog-flow > div > div { + background-color: white; + height: calc(100% - 338px); + width: 100%; } \ No newline at end of file diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.js b/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.js index 9295ac2..5062ed2 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.js @@ -1,6 +1,7 @@ import React, {Component} from 'react'; import './FrameProfile.css'; import FrameStepDetail from "./FrameStepDetail/FrameStepDetail"; +import XlogFlow from "./XlogFlow/XlogFlow"; import {connect} from 'react-redux'; import {withRouter} from 'react-router-dom'; import * as d3 from "d3"; @@ -30,7 +31,11 @@ class FrameProfile extends Component { this.state = { selectedStepIndex: null, - lastResizeTime : null + lastResizeTime : null, + flow : { + show : false, + parameter : {} + } }; } @@ -45,6 +50,16 @@ class FrameProfile extends Component { if (nextProps.rightWidth !== this.props.rightWidth) { this.resize(); } + if(nextProps.steps !== this.props.steps){ + this.setState({ + selectedStepIndex: null, + flow : { + show : false, + parameter : {} + } + }) + } + } init = (props) => { @@ -250,6 +265,11 @@ class FrameProfile extends Component { selectedStepIndex: index }); }; + showFlowClose =(flow) =>{ + this.setState({ + flow : flow + }); + }; //sql3 literal bind? getElapsedTime = (row) => { @@ -416,10 +436,16 @@ class FrameProfile extends Component { } }; onClickTxToXFlow=(gxid,txid,caller,endtime) =>{ - console.log('onClickTxToXFlow',gxid,txid,caller,endtime); const yyyymmdd = moment(new Date(Number(endtime))).format("YYYYMMDD"); - const url=`/scouter/v1/xlog-data/${yyyymmdd}/gxid/${gxid}`; - console.log(url); + this.setState({ + flow : { + show : true, + gxid : gxid, + txid : txid, + caller : caller, + yyyymmdd : yyyymmdd + } + }); }; @@ -581,7 +607,18 @@ class FrameProfile extends Component { toggleFormatter={this.props.toggleFormatter} toggleBind={this.props.toggleBind} toggleWrap={this.props.toggleWrap}>
-
} +
+ } + { + this.state.flow.show && +
+
+ + +
+
+ } + ); } diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.css b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.css new file mode 100644 index 0000000..e32d2bf --- /dev/null +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.css @@ -0,0 +1,25 @@ +.xlog-flow { + position: relative; +} +.xlog-flow .title { + padding: 8px; + background-color: #333; + color: #fff; + height: 36px; + box-sizing: border-box; +} +.xlog-flow .title span { + display: inline-block; + padding: 4px; +} +.xlog-flow .close-btn { + position: absolute; + top: 7px; + right: 5px; + display: inline-block !important; +} +.xlog-flow .flow-content { + height: 100%; + overflow-y: auto; + width: 100%; +} diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js new file mode 100644 index 0000000..e04fdb6 --- /dev/null +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js @@ -0,0 +1,56 @@ +import React,{Component} from "react"; +import './XlogFlow.css' +import {connect} from 'react-redux'; +import {withRouter} from 'react-router-dom'; +import XlogFlowChart from './XlogFlowChart/XlogFlowChart' +import XlogFlowRender from './XlogFlowChart/XlogFlowRender' + +// const url=`/scouter/v1/xlog-data/${yyyymmdd}/gxid/${gxid}`; +class XlogFlow extends Component { + + state = { + data: [] + }; + + constructor(props) { + super(props); + + }; + +//-- event list + close= () =>{ + this.props.close({ + flow : { + show : false, + parameter : {} + } + }); + }; + +//- render + render() { + + const {data} = this.state; + + return( +
+
+
+
+
+ + + +
+
+ ); + } +} +const mapStateToProps = (state) => { + return { + config: state.config + }; +}; + +XlogFlow = connect(mapStateToProps, undefined)(XlogFlow); +export default withRouter(XlogFlow); \ No newline at end of file diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.styl b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.styl new file mode 100644 index 0000000..e897634 --- /dev/null +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.styl @@ -0,0 +1,20 @@ +.xlog-flow + position: relative + .title + padding: 8px + background-color: #333 + color: white + height: 36px + box-sizing: border-box + & span + display: inline-block + padding: 4px + .close-btn + position: absolute + top : 7px + right : 5px + display: inline-block !important + .flow-content + height: 100% + overflow-y: auto + width: 100% diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowChart.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowChart.js new file mode 100644 index 0000000..64f5a45 --- /dev/null +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowChart.js @@ -0,0 +1,28 @@ +import React from 'react' +import connect from "react-redux/es/connect/connect"; +import {withRouter} from "react-router-dom"; + +function XlogFlowChart({ width, height, children }) { + return ( + + { + children + } + + ) +} + +// XlogFlowChart.propTypes = { +// // width: PropTypes.number.isRequired, +// // height: PropTypes.number.isRequired, +// // children: PropTypes.node, +// }; + +const mapStateToProps = (state) => { + return { + config: state.config + }; +}; + +XlogFlowChart = connect(mapStateToProps, undefined)(XlogFlowChart); +export default withRouter(XlogFlowChart); \ No newline at end of file diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowRender.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowRender.js new file mode 100644 index 0000000..a1f68db --- /dev/null +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowRender.js @@ -0,0 +1,48 @@ +import connect from "react-redux/es/connect/connect"; +import {withRouter} from "react-router-dom"; +import React,{Component} from "react"; +import * as d3 from "d3"; + +class XlogFlowRender extends Component { + state = { + g: null, + }; + + constructor(props) { + super(props); + + }; + + shouldComponentUpdate() { + // we will handle moving the nodes on our own with d3.js + // make React ignore this component + return false; + } + + onRef = (ref) => { + this.setState({ g : d3.select(ref) }, () => this.renderflow(this.props.data)) + }; + + renderflow = (data) =>{ + + }; + + + render() { + + return ( + + ) + }; + +} + + +const mapStateToProps = (state) => { + return { + config: state.config + }; +}; + +XlogFlowRender = connect(mapStateToProps, undefined)(XlogFlowRender); +export default withRouter(XlogFlowRender); \ No newline at end of file From b04508c8fd350d2b47a7de768492fdcc37bd58a9 Mon Sep 17 00:00:00 2001 From: "yeosong.heo" Date: Tue, 15 Oct 2019 06:06:43 +0900 Subject: [PATCH 31/77] :construction: tx load dep model --- http/xflow.http | 96 +++++++++------ .../Profiler/FrameProfile/FrameProfile.js | 10 +- .../FrameProfile/XlogFlow/XlogFlow.js | 116 +++++++++++++++++- 3 files changed, 178 insertions(+), 44 deletions(-) diff --git a/http/xflow.http b/http/xflow.http index dd2a4fd..009414a 100644 --- a/http/xflow.http +++ b/http/xflow.http @@ -1,40 +1,60 @@ -GET http://demo.scouterapm.com:6188/scouter/v1/xlog-data/20191010/gxid/-2608246922300427269 +GET http://demo.scouterapm.com:6188/scouter/v1/xlog-data/20191015/gxid/899870199175109144 ### -GET http://demo.scouterapm.com:6188/scouter/v1/profile-data/20191010/-2608246922300427269 - +#"result": [ +# { +# "endTime": "1571082075636", +# "objHash": "1026166527", +# "service": "http:/traceability/cross-service/simple2", +# "txid": "6990900342269582982", +# "threadName": "", +# "caller": "899870199175109144", +# "gxid": "899870199175109144", +# "elapsed": "578", +# "error": "", +# "cpu": "0", +# "sqlCount": "0", +# "sqlTime": "0", +# "ipAddr": "0.0.0.0", +# "allocatedMemory": "0", +# "internalId": "0", +# "userAgent": "", +# "referrer": "", +# "group": "", +# "apicallCount": "0", +# "apicallTime": "0", +# "countryCode": "", +# "city": "", +# "login": "", +# "desc": "", +# "hasDump": "0", +# "text1": "ip-172-31-21-206.ap-northeast-2.compute.internal:ScouterDemoWeb", +# "text2": "", +# "text3": "", +# "text4": "", +# "text5": "", +# "queuingHost": "", +# "queuingTime": "0", +# "queuing2ndHost": "", +# "queuing2ndTime": "0", +# "xlogType": "ZIPKIN_SPAN" +# }, +GET http://demo.scouterapm.com:6188/scouter/v1/profile-data/20191015/899870199175109144 ### - -#filter -> -# xlogType : - -# public final static byte WEB_SERVICE = 0; -# public final static byte APP_SERVICE = 1; -# public final static byte BACK_THREAD = 2; -# public final static byte ASYNCSERVLET_DISPATCHED_SERVICE = 3; -# public final static byte BACK_THREAD2 = 4; -# public final static byte ZIPKIN_SPAN = 5; -################## -# public final static byte METHOD = 1; -# public final static byte METHOD2 = 10; -# public final static byte SPAN = 51; -# public final static byte SQL = 2; -# public final static byte SQL2 = 8; -# public final static byte SQL3 = 16; -# public final static byte MESSAGE = 3; -# public final static byte SOCKET = 5; -# public final static byte APICALL = 6; -# public final static byte APICALL2 = 15; -# public final static byte SPANCALL = 52; -# public final static byte THREAD_SUBMIT = 7; -# public final static byte HASHED_MESSAGE = 9; -# public final static byte PARAMETERIZED_MESSAGE = 17; -# public final static byte DUMP = 12; -# public final static byte DISPATCH = 13; -# public final static byte THREAD_CALL_POSSIBLE = 14; -# -# public final static byte METHOD_SUM = 11; -# public final static byte SQL_SUM = 21; -# public final static byte MESSAGE_SUM = 31; -# public final static byte SOCKET_SUM = 42; -# public final static byte APICALL_SUM = 43; -# public final static byte CONTROL = 99; \ No newline at end of file +#"result": [ +# { +# "mainValue": "[driving thread] http-nio-8080-exec-21", +# "additionalValueList": [], +# "step": { +# "parent": "-1", +# "index": "0", +# "start_time": "0", +# "start_cpu": "0", +# "spanPack": null, +# "hash": "1712797862", +# "time": "-1", +# "value": "0", +# "stepType": "9", +# "order": "0", +# "stepTypeName": "HASHED_MESSAGE" +# } +# }, \ No newline at end of file diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.js b/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.js index 5062ed2..0b29c95 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.js @@ -440,10 +440,12 @@ class FrameProfile extends Component { this.setState({ flow : { show : true, - gxid : gxid, - txid : txid, - caller : caller, - yyyymmdd : yyyymmdd + parameter : { + gxid : gxid, + txid : txid, + caller : caller, + yyyymmdd : yyyymmdd + } } }); diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js index e04fdb6..d419a5c 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js @@ -1,9 +1,13 @@ import React,{Component} from "react"; +import jQuery from "jquery"; import './XlogFlow.css' import {connect} from 'react-redux'; import {withRouter} from 'react-router-dom'; import XlogFlowChart from './XlogFlowChart/XlogFlowChart' import XlogFlowRender from './XlogFlowChart/XlogFlowRender' +import {getCurrentUser, getHttpProtocol, getWithCredentials, setAuthHeader} from "../../../../../../common/common"; +import {addRequest, setControlVisibility} from "../../../../../../actions"; +import moment from "moment/moment"; // const url=`/scouter/v1/xlog-data/${yyyymmdd}/gxid/${gxid}`; class XlogFlow extends Component { @@ -16,6 +20,106 @@ class XlogFlow extends Component { super(props); }; + componentDidMount() { + // console.log("componentDidMount"); + //- first create event + window.addEventListener("resize", this.resize); + this.loadByGxId(); + } + + componentWillUnmount(){ + + } + + componentWillReceiveProps(nextProps){ + console.log("componentWillReceiveProps"); + } + + resize = () =>{ + + }; + + tryDepFlowSearch(globalTracing){ + //next try step + const {config, user} = this.props; + const _promise = globalTracing.map(_tx =>{ + const yyyymmdd = moment(new Date(Number(_tx.endTime))).format("YYYYMMDD"); + const _url = `${getHttpProtocol(config)}/scouter/v1/profile-data/${yyyymmdd}/${_tx.txid}`; + return jQuery.ajax({ + method: "GET", + async: true, + dataType: 'text', + url: _url, + xhrFields: getWithCredentials(config), + beforeSend: function (xhr) { + setAuthHeader(xhr, config, getCurrentUser(config, user)); + } + }); + }); + jQuery.when(_promise).then(result=>{ + const serviceMap = globalTracing.map(_global =>{ + const map = {}; + return map[_global.txid] = { + name : _global.service, + objHash : _global.objHash, + txid : _global.txid, + error : _global.error, + xType : _global.xlogType, + elapsed : new Number(_global.elapsed), + threadName : _global.threadName, + tag : { + caller : _global.caller, + ip : _global.ipAddr + } + } + }); + let index = 0; + const depSteps= result.map(_data =>{ + return JSON.parse(_data.responseText); + }).map(_data =>{ + const _ret = { + txid : serviceMap[index].txid, + steps: _data + }; + index++; + return _ret; + }) + console.log(serviceMap,depSteps); + }); + + } + + loadByGxId(){ + this.props.setControlVisibility("Loading", true); + this.props.addRequest(); + const {config, user,flow } = this.props; + // console.log("loadByGxId",flow,user); + const _url = `${getHttpProtocol(config)}/scouter/v1/xlog-data/${flow.parameter.yyyymmdd}/gxid/${flow.parameter.gxid}`; + jQuery.ajax({ + method: "GET", + async: true, + dataType: 'text', + url: _url, + xhrFields: getWithCredentials(config), + beforeSend: function (xhr) { + setAuthHeader(xhr, config, getCurrentUser(config, user)); + } + }).done((msg) => { + const reponseObj = JSON.parse(msg); + if(reponseObj.status === "200"){ + this.tryDepFlowSearch(reponseObj.result); + } + }).always(() => { + this.props.setControlVisibility("Loading", false); + }); + }; + shouldComponentUpdate(nextProps,nextState) { + if( nextProps.flow !== this.props.flow) { + return false; + } + return true; + } + //-- event list close= () =>{ @@ -48,9 +152,17 @@ class XlogFlow extends Component { } const mapStateToProps = (state) => { return { - config: state.config + config: state.config, + user: state.user, + objects: state.target.objects + }; +}; +let mapDispatchToProps = (dispatch) => { + return { + addRequest: () => dispatch(addRequest()), + setControlVisibility: (name, value) => dispatch(setControlVisibility(name, value)), }; }; -XlogFlow = connect(mapStateToProps, undefined)(XlogFlow); +XlogFlow = connect(mapStateToProps, mapDispatchToProps)(XlogFlow); export default withRouter(XlogFlow); \ No newline at end of file From fa4a0aae9319983e5546770b677bac76a1c10ffc Mon Sep 17 00:00:00 2001 From: kranian Date: Tue, 15 Oct 2019 18:06:55 +0900 Subject: [PATCH 32/77] :construction: tx flow paring .. --- src/components/Controller/Controller.js | 12 +- .../XlogFlow/DependencyElement.js | 33 ++ .../FrameProfile/XlogFlow/XlogFlow.js | 328 ++++++++++++++++-- 3 files changed, 338 insertions(+), 35 deletions(-) create mode 100644 src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/DependencyElement.js diff --git a/src/components/Controller/Controller.js b/src/components/Controller/Controller.js index 24aa02f..fb18c02 100644 --- a/src/components/Controller/Controller.js +++ b/src/components/Controller/Controller.js @@ -216,19 +216,23 @@ class Controller extends Component { doneCount++; if (msg.result && msg.result.length > 0) { - serverNameMap[server.key] = msg.result[0].name; + serverNameMap[server.key] = { info : msg.result[0]}; + } else { - serverNameMap[server.key] = ""; + serverNameMap[server.key] = {}; } }).fail(() => { doneCount++; - serverNameMap[server.key] = "FAILED TO GET NAME"; + serverNameMap[server.key] = { + info : { name : "FAILED TO GET NAME", id : "-1"} + }; }).always(() => { if (doneCount >= allCount) { let _conf = _.clone(props.config); _conf.servers.forEach((server, idx) => { if (serverNameMap[idx]) { - server.name = `${serverNameMap[idx]} (${server.name})`; + server.name = `${serverNameMap[idx].info.name} (${server.name})`; + server.id = serverNameMap[idx].info.id; } }); props.setConfig(_conf); diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/DependencyElement.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/DependencyElement.js new file mode 100644 index 0000000..9f73c6f --- /dev/null +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/DependencyElement.js @@ -0,0 +1,33 @@ +export default class DependencyElement { + name = null; + elapsed=-1; + error=""; + xtype=""; + address=""; + threadName=""; +//- + type; + id; + dupleCnt = 1; +//-- add + children; + tags = {}; + constructor(obj={type:"0", id: "-1"}){ + this.id = obj.id; + this.type = obj.type; + this.children = new Map(); + }; + + addChild(child){ + const childObj = this.children.get(child.id); + if(childObj){ + childObj.dupleCnt += child.dupleCnt; + childObj.elapsed += child.elapsed; + if (!child.error) { + childObj.error = child.error; + } + }else{ + this.children.set(child.id,child); + } + } +} \ No newline at end of file diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js index d419a5c..4d40f11 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js @@ -5,11 +5,147 @@ import {connect} from 'react-redux'; import {withRouter} from 'react-router-dom'; import XlogFlowChart from './XlogFlowChart/XlogFlowChart' import XlogFlowRender from './XlogFlowChart/XlogFlowRender' +import DependencyElement from "./DependencyElement"; + import {getCurrentUser, getHttpProtocol, getWithCredentials, setAuthHeader} from "../../../../../../common/common"; import {addRequest, setControlVisibility} from "../../../../../../actions"; import moment from "moment/moment"; + // const url=`/scouter/v1/xlog-data/${yyyymmdd}/gxid/${gxid}`; + +const XLogTypes= { + WEB_SERVICE : "WEB_SERVICE", + APP_SERVICE : "APP_SERVICE", + BACK_THREAD : "BACK_THREAD", + ASYNCSERVLET_DISPATCHED_SERVICE : "ASYNCSERVLET_DISPATCHED_SERVICE", + BACK_THREAD2 : "BACK_THREAD2", + ZIPKIN_SPAN : "ZIPKIN_SPAN", + UNKNOWN : "UNKNOWN", + + toNumber(value="UNKNOWN"){ + switch(value){ + case this.WEB_SERVICE : return 0; + case this.APP_SERVICE : return 1; + case this.BACK_THREAD : return 2; + case this.ASYNCSERVLET_DISPATCHED_SERVICE : return 3; + case this.BACK_THREAD2 : return 4; + case this.ZIPKIN_SPAN : return 5; + default: + return 99; + } + } +}; +const Steps = { + METHOD : "1", + METHOD2 : "10", + SPAN : "51", + SQL : "2", + SQL2 : "8", + SQL3 : "16", + MESSAGE : "3", + SOCKET : "5", + APICALL : "6", + APICALL2 : "15", + SPANCALL : "52", + THREAD_SUBMIT : "7", + HASHED_MESSAGE : "9", + PARAMETERIZED_MESSAGE : "17", + DUMP : "12", + DISPATCH : "13", + THREAD_CALL_POSSIBLE : "14", + METHOD_SUM : "11", + SQL_SUM : "21", + MESSAGE_SUM : "31", + SOCKET_SUM : "42", + APICALL_SUM : "43", + CONTROL : "99", + + toString(value){ + switch (value) { + case this.METHOD : return "METHOD"; + case this.METHOD2 : return "METHOD2"; + case this.SPAN : return "SPAN"; + case this.SQL : return "SQL"; + case this.SQL2 : return "SQL2"; + case this.SQL3 : return "SQL3"; + case this.MESSAGE : return "MESSAGE"; + case this.SOCKET : return "SOCKET"; + case this.APICALL : return "APICALL"; + case this.APICALL2 : return "APICALL2"; + case this.SPANCALL : return "SPANCALL"; + case this.THREAD_SUBMIT : return "THREAD_SUBMIT"; + case this.HASHED_MESSAGE : return "HASHED_MESSAGE"; + case this.PARAMETERIZED_MESSAGE : return "PARAMETERIZED_MESSAGE"; + case this.DUMP : return "DUMP"; + case this.DISPATCH : return "DISPATCH"; + case this.THREAD_CALL_POSSIBLE : return "THREAD_CALL_POSSIBLE"; + case this.METHOD_SUM : return "METHOD_SUM"; + case this.SQL_SUM : return "SQL_SUM"; + case this.MESSAGE_SUM : return "MESSAGE_SUM"; + case this.SOCKET_SUM : return "SOCKET_SUM"; + case this.APICALL_SUM : return "APICALL_SUM"; + case this.CONTROL : return "CONTROL"; + + } + }, + toElapsedTime(step){ + switch (step.stepType) { + case this.HASHED_MESSAGE : + return Number(step.time) >= 0 ? Number(step.time) : 0; + case this.DUMP : + case this.MESSAGE_SUM : + case this.CONTROL : + return 0; + case this.SQL3 : + case this.SQL : + case this.SQL2: + case this.METHOD : + case this.SPAN : + case this.SPANCALL : + case this.METHOD2 : + case this.MESSAGE : + case this.SOCKET : + case this.APICALL : + case this.APICALL2 : + case this.THREAD_SUBMIT : + case this.PARAMETERIZED_MESSAGE : + case this.DISPATCH : + case this.THREAD_CALL_POSSIBLE : + case this.METHOD_SUM : + case this.SQL_SUM : + case this.SOCKET_SUM : + case this.APICALL_SUM : + return Number(step.elapsed); + default : + return Number(step.elapsed); + } + + } +}; + +const ElementType = { + USER : "0", + SERVICE : "1", + API_CALL : "2", + SQL : "3", + DISPATCH : "4", + THREAD : "5", + + toString(value) { + switch(value){ + case this.USER : return "USER"; + case this.SERVICE : return "SERVICE"; + case this.API_CALL : return "API_CALL"; + case this.SQL : return "SQL"; + case this.DISPATCH : return "DISPATCH"; + case this.THREAD : return "THREAD"; + default: + return "UNKNOWN"; + } + } +}; + class XlogFlow extends Component { state = { @@ -38,11 +174,71 @@ class XlogFlow extends Component { resize = () =>{ }; - + stepToElement(serviceMap,thisElement,steps){ + const {stepType} = steps.step; + const {step,mainValue} = steps; + switch(stepType){ + case Steps.APICALL: + case Steps.APICALL2: + const apiElement = new DependencyElement(ElementType.API_CALL,step.txid + step.hash); + apiElement.elapsed= Steps.toElapsedTime(steps.step); + apiElement.error = step.error; + apiElement.name = mainValue; + apiElement.address = step.address; + if(steps.step.txid !== "0"){ + //other call check + const {serviceElement} = serviceMap.get(step.txid); + if(serviceElement){ + serviceElement.address = step.address; + thisElement.addChild(serviceElement); + }else{ + thisElement.addChild(apiElement); + } + }else{ + thisElement.addChild(apiElement); + } + break; + case Steps.SPANCALL: + const spanCallElement = new DependencyElement(ElementType.API_CALL, step.txid + step.hash); + spanCallElement.elapsed = step.elapsed; + spanCallElement.error = step.error; + spanCallElement.name = mainValue; + spanCallElement.address = step.address; + if (step.txid != 0) { + const {serviceElement} = serviceMap.get(step.txid); + if (serviceElement) { + serviceElement.address = step.address; + thisElement.addChild(serviceElement); + } else { + thisElement.addChild(spanCallElement); + } + } else { + thisElement.addChild(spanCallElement); + } + break; + case Steps.DISPATCH: + break; + case Steps.THREAD_CALL_POSSIBLE: + break; + case Steps.APICALL_SUM: + break; + case Steps.SQL: + case Steps.SQL2: + case Steps.SQL3: + break; + case Steps.SQL_SUM: + break; + case Steps.THREAD_SUBMIT: + //-analys + break; + + + } + } tryDepFlowSearch(globalTracing){ //next try step const {config, user} = this.props; - const _promise = globalTracing.map(_tx =>{ + const _allofTrace = globalTracing.map(_tx =>{ const yyyymmdd = moment(new Date(Number(_tx.endTime))).format("YYYYMMDD"); const _url = `${getHttpProtocol(config)}/scouter/v1/profile-data/${yyyymmdd}/${_tx.txid}`; return jQuery.ajax({ @@ -51,40 +247,110 @@ class XlogFlow extends Component { dataType: 'text', url: _url, xhrFields: getWithCredentials(config), - beforeSend: function (xhr) { + beforeSend: (xhr)=>{ setAuthHeader(xhr, config, getCurrentUser(config, user)); } }); }); - jQuery.when(_promise).then(result=>{ - const serviceMap = globalTracing.map(_global =>{ - const map = {}; - return map[_global.txid] = { - name : _global.service, - objHash : _global.objHash, - txid : _global.txid, - error : _global.error, - xType : _global.xlogType, - elapsed : new Number(_global.elapsed), - threadName : _global.threadName, - tag : { - caller : _global.caller, - ip : _global.ipAddr + const _allofObject = this.props.config.servers.filter(_server =>_server.default) + .map(_server =>{ + return jQuery.ajax({ + method: "GET", + async: true, + url: `${getHttpProtocol(this.props.config)}/scouter/v1/object?serverId=${_server.id}`, + xhrFields: getWithCredentials(this.props.config), + beforeSend: (xhr) =>{ + setAuthHeader(xhr, this.props.config, getCurrentUser(this.props.config, this.props.user)); + } + }); + }); + + jQuery.when(_allofTrace,_allofObject).then((fullTrace,fullObject)=>{ + try { + const _objects = new Map(); + const _serviceMap = new Map(); + const _stepMap = new Map(); + const _rootMap = new Map(); + + fullObject.map(_data =>JSON.parse(_data.responseText)) + .flatMap(_data => _data.result) + .forEach(_obj => { + const _ret = {}; + _objects.set(_obj.objHash,_obj); + }); + const _order = globalTracing.map(_global => { + let excludeObjName = false; + let eType = ElementType.SERVICE; + switch(_global.xlogType){ + case XLogTypes.WEB_SERVICE: + eType = ElementType.DISPATCH; + excludeObjName = true; + break; + case XLogTypes.BACK_THREAD2: + excludeObjName = true; + eType = ElementType.THREAD; + break; + } + const serviceElement = new DependencyElement({type:eType , id : _global.txid}); + if(excludeObjName) { + serviceElement.name = _global.service; + } else { + const _object = _objects.get(_global.objHash); + const _name = _object.objName ? _object.objName : 'unknown'; + serviceElement.name = `${_global.service}\n(" ${_name} ")`; + } + + serviceElement.elapsed = new Number(_global.elapsed); + serviceElement.error = _global.error; + serviceElement.threadName = _global.threadName; + serviceElement.xType = _global.xlogType; + serviceElement.tags = { + caller: _global.caller, + ip: _global.ipAddr + }; + + _serviceMap.set(_global.txid, { + serviceElement : serviceElement + }); + + return { + txid : _global.txid + }; + }); + let index = 0; + fullTrace.map(_data =>JSON.parse(_data.responseText)) + .forEach(_data => { + _stepMap.set(_order[index].txid,_data.result); + index++; + }); +//-- iter + _order.forEach(_tx =>{ + const {txid} = _tx; + const {serviceElement} = _serviceMap.get(txid); + if(!serviceElement){ + return; } - } - }); - let index = 0; - const depSteps= result.map(_data =>{ - return JSON.parse(_data.responseText); - }).map(_data =>{ - const _ret = { - txid : serviceMap[index].txid, - steps: _data - }; - index++; - return _ret; - }) - console.log(serviceMap,depSteps); + const steps = _stepMap.get(txid); + if(!steps){ + return; + } + steps.forEach(_step => this.stepToElement(_serviceMap,serviceElement,_step)); + if( _serviceMap.size === 1 || serviceElement.tags.caller === "0"){ + const {ip} = serviceElement.tags; + if(ip){ + let ipElement = _rootMap.get(ip); + if(!ipElement){ + ipElement = new DependencyElement({type:ElementType.USER , id : ip}); + _rootMap.set(ip, ipElement); + } + ipElement.addChild(serviceElement); + } + } + }); + console.log(_rootMap); + }catch (e) { + console.log('error =>',e); + } }); } From f115cdbfab4b84c13d28cd0f9822e147c07ec811 Mon Sep 17 00:00:00 2001 From: "yeosong.heo" Date: Wed, 16 Oct 2019 05:41:19 +0900 Subject: [PATCH 33/77] :construction: tx flow model gen --- .../XlogFlow/DependencyElement.js | 3 +- .../FrameProfile/XlogFlow/XlogFlow.js | 282 +++++++++++------- 2 files changed, 172 insertions(+), 113 deletions(-) diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/DependencyElement.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/DependencyElement.js index 9f73c6f..8feeb5e 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/DependencyElement.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/DependencyElement.js @@ -1,6 +1,6 @@ export default class DependencyElement { name = null; - elapsed=-1; + elapsed=0; error=""; xtype=""; address=""; @@ -30,4 +30,5 @@ export default class DependencyElement { this.children.set(child.id,child); } } + } \ No newline at end of file diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js index 4d40f11..1d2e4e3 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js @@ -9,6 +9,7 @@ import DependencyElement from "./DependencyElement"; import {getCurrentUser, getHttpProtocol, getWithCredentials, setAuthHeader} from "../../../../../../common/common"; import {addRequest, setControlVisibility} from "../../../../../../actions"; +import * as _ from "lodash"; import moment from "moment/moment"; @@ -174,23 +175,29 @@ class XlogFlow extends Component { resize = () =>{ }; + stringTruncate(str,len){ + return !str || str.length <= len ? str : str.substring(0, len); + } + toDependencyElement(type,id){ + return new DependencyElement({type : type , id : id}); + } stepToElement(serviceMap,thisElement,steps){ const {stepType} = steps.step; const {step,mainValue} = steps; switch(stepType){ case Steps.APICALL: case Steps.APICALL2: - const apiElement = new DependencyElement(ElementType.API_CALL,step.txid + step.hash); - apiElement.elapsed= Steps.toElapsedTime(steps.step); + const apiElement = this.toDependencyElement(ElementType.API_CALL, step.txid + step.hash); + apiElement.elapsed= Steps.toElapsedTime(step); apiElement.error = step.error; apiElement.name = mainValue; apiElement.address = step.address; - if(steps.step.txid !== "0"){ + if(step.txid !== "0"){ //other call check - const {serviceElement} = serviceMap.get(step.txid); - if(serviceElement){ - serviceElement.address = step.address; - thisElement.addChild(serviceElement); + const callElement = serviceMap.get(step.txid); + if(callElement){ + callElement.serviceElement.address = step.address; + thisElement.addChild(callElement.serviceElement); }else{ thisElement.addChild(apiElement); } @@ -199,16 +206,16 @@ class XlogFlow extends Component { } break; case Steps.SPANCALL: - const spanCallElement = new DependencyElement(ElementType.API_CALL, step.txid + step.hash); - spanCallElement.elapsed = step.elapsed; + const spanCallElement = this.toDependencyElement(ElementType.API_CALL, step.txid + step.hash); + spanCallElement.elapsed = Steps.toElapsedTime(step); spanCallElement.error = step.error; spanCallElement.name = mainValue; spanCallElement.address = step.address; - if (step.txid != 0) { - const {serviceElement} = serviceMap.get(step.txid); - if (serviceElement) { - serviceElement.address = step.address; - thisElement.addChild(serviceElement); + if (step.txid !== "0") { + const callElement = serviceMap.get(step.txid); + if (callElement) { + callElement.serviceElement.address = step.address; + thisElement.addChild(callElement.serviceElement); } else { thisElement.addChild(spanCallElement); } @@ -217,24 +224,111 @@ class XlogFlow extends Component { } break; case Steps.DISPATCH: + const dispatchElement = this.toDependencyElement(ElementType.DISPATCH, step.txid + step.hash); + dispatchElement.elapsed = Steps.toElapsedTime(step); + dispatchElement.error = step.error; + dispatchElement.name = mainValue + if (step.txid !== "0") { + const callElement = serviceMap.get(step.txid); + if (callElement) { + thisElement.addChild(callElement.serviceElement); + } else { + thisElement.addChild(dispatchElement); + } + } else { + thisElement.addChild(dispatchElement); + } break; case Steps.THREAD_CALL_POSSIBLE: - break; - case Steps.APICALL_SUM: + //- other thread call checking + const tcElement = this.toDependencyElement(ElementType.DISPATCH, step.txid + step.hash); + tcElement.elapsed = Steps.toElapsedTime(step); + tcElement.name = mainValue; + if (step.txid !== "0") { + const callElement = serviceMap.get(step.txid); + if (callElement) { + thisElement.addChild(callElement.serviceElement); + } else { + thisElement.addChild(tcElement); + } + } else { + thisElement.addChild(tcElement); + } + break; + case Steps.APICALL_SUM: + const apiSumElement = this.toDependencyElement(ElementType.API_CALL, step.hash); + apiSumElement.dupleCnt = step.count; + apiSumElement.elapsed = Steps.toElapsedTime(step); + apiSumElement.error = step.error; + apiSumElement.name = mainValue; + thisElement.addChild(apiSumElement); break; case Steps.SQL: case Steps.SQL2: case Steps.SQL3: + const sqlElement = this.toDependencyElement(ElementType.SQL, step.hash); + sqlElement.elapsed = Steps.toElapsedTime(step); + sqlElement.name = `${this.stringTruncate(mainValue, 20)}...`; + sqlElement.error = step.error; + sqlElement.tags.sql = mainValue; + thisElement.addChild(sqlElement); break; case Steps.SQL_SUM: + const sqlSumElement = this.toDependencyElement(ElementType.SQL, step.hash); + sqlSumElement.dupleCnt = step.count; + sqlSumElement.elapsed = Steps.toElapsedTime(step); + sqlSumElement.error = step.error; + sqlSumElement.name = `${this.stringTruncate(mainValue, 20)}...`; + sqlSumElement.tags.sql=mainValue; + thisElement.addChild(sqlSumElement); break; - case Steps.THREAD_SUBMIT: - //-analys - break; - - + // case Steps.THREAD_SUBMIT: + // -analys + // break; } } + flowOrder(globalTracing,serviceMap,objects){ + return globalTracing.map(_global => { + let excludeObjName = false; + let eType = ElementType.SERVICE; + switch(_global.xlogType){ + case XLogTypes.WEB_SERVICE: + eType = ElementType.DISPATCH; + excludeObjName = true; + break; + case XLogTypes.BACK_THREAD2: + excludeObjName = true; + eType = ElementType.THREAD; + break; + } + const serviceElement = new DependencyElement({type:eType , id : _global.txid}); + if(excludeObjName) { + serviceElement.name = _global.service; + } else { + const _object = objects.get(_global.objHash); + + const _name = _object.objName ? _object.objName : 'unknown'; + serviceElement.name = `${_global.service}\n(" ${_name} ")`; + } + + serviceElement.elapsed = new Number(_global.elapsed); + serviceElement.error = _global.error; + serviceElement.threadName = _global.threadName; + serviceElement.xtype = _global.xlogType; + serviceElement.tags = { + caller: _global.caller, + ip: _global.ipAddr + }; + + serviceMap.set(_global.txid, { + serviceElement : serviceElement + }); + + return { + txid : _global.txid + }; + }); + } tryDepFlowSearch(globalTracing){ //next try step const {config, user} = this.props; @@ -244,7 +338,8 @@ class XlogFlow extends Component { return jQuery.ajax({ method: "GET", async: true, - dataType: 'text', + // dataType: 'text', + dataType: "json", url: _url, xhrFields: getWithCredentials(config), beforeSend: (xhr)=>{ @@ -257,6 +352,7 @@ class XlogFlow extends Component { return jQuery.ajax({ method: "GET", async: true, + dataType: "json", url: `${getHttpProtocol(this.props.config)}/scouter/v1/object?serverId=${_server.id}`, xhrFields: getWithCredentials(this.props.config), beforeSend: (xhr) =>{ @@ -264,95 +360,58 @@ class XlogFlow extends Component { } }); }); - - jQuery.when(_allofTrace,_allofObject).then((fullTrace,fullObject)=>{ - try { - const _objects = new Map(); - const _serviceMap = new Map(); - const _stepMap = new Map(); - const _rootMap = new Map(); - - fullObject.map(_data =>JSON.parse(_data.responseText)) - .flatMap(_data => _data.result) - .forEach(_obj => { - const _ret = {}; - _objects.set(_obj.objHash,_obj); - }); - const _order = globalTracing.map(_global => { - let excludeObjName = false; - let eType = ElementType.SERVICE; - switch(_global.xlogType){ - case XLogTypes.WEB_SERVICE: - eType = ElementType.DISPATCH; - excludeObjName = true; - break; - case XLogTypes.BACK_THREAD2: - excludeObjName = true; - eType = ElementType.THREAD; - break; - } - const serviceElement = new DependencyElement({type:eType , id : _global.txid}); - if(excludeObjName) { - serviceElement.name = _global.service; - } else { - const _object = _objects.get(_global.objHash); - const _name = _object.objName ? _object.objName : 'unknown'; - serviceElement.name = `${_global.service}\n(" ${_name} ")`; - } - - serviceElement.elapsed = new Number(_global.elapsed); - serviceElement.error = _global.error; - serviceElement.threadName = _global.threadName; - serviceElement.xType = _global.xlogType; - serviceElement.tags = { - caller: _global.caller, - ip: _global.ipAddr - }; - - _serviceMap.set(_global.txid, { - serviceElement : serviceElement - }); - - return { - txid : _global.txid - }; - }); - let index = 0; - fullTrace.map(_data =>JSON.parse(_data.responseText)) - .forEach(_data => { - _stepMap.set(_order[index].txid,_data.result); - index++; - }); + const _objects = new Map(); + const _serviceMap = new Map(); + const _stepMap = new Map(); + const _rootMap = new Map(); + jQuery.when(_allofObject[0],..._allofTrace) + .done((fullObject,...fullTrace)=> { + try{ + + fullObject.forEach(_res => { + _.forEach(_res.result,(_obj) =>_objects.set(_obj.objHash,_obj)); + }); + + const _order = this.flowOrder(globalTracing,_serviceMap,_objects); + let index = 0; + fullTrace.forEach(_res =>{ + _stepMap.set(_order[index].txid,_res[0].result); + index++; + }); //-- iter - _order.forEach(_tx =>{ - const {txid} = _tx; - const {serviceElement} = _serviceMap.get(txid); - if(!serviceElement){ - return; - } - const steps = _stepMap.get(txid); - if(!steps){ - return; - } - steps.forEach(_step => this.stepToElement(_serviceMap,serviceElement,_step)); - if( _serviceMap.size === 1 || serviceElement.tags.caller === "0"){ - const {ip} = serviceElement.tags; - if(ip){ - let ipElement = _rootMap.get(ip); - if(!ipElement){ - ipElement = new DependencyElement({type:ElementType.USER , id : ip}); - _rootMap.set(ip, ipElement); - } - ipElement.addChild(serviceElement); - } - } - }); - console.log(_rootMap); - }catch (e) { - console.log('error =>',e); - } - }); - + _order.forEach(_tx =>{ + const {txid} = _tx; + const {serviceElement} = _serviceMap.get(txid); + if(!serviceElement){ + return; + } + const steps = _stepMap.get(txid); + if(!steps){ + return; + } + steps.forEach(_step => this.stepToElement(_serviceMap,serviceElement,_step)); + if( _serviceMap.size === 1 || serviceElement.tags.caller === "0"){ + const {ip} = serviceElement.tags; + if(ip){ + let ipElement = _rootMap.get(ip); + if(!ipElement){ + ipElement = this.toDependencyElement(ElementType.USER,ip); + ipElement.name = ip; + _rootMap.set(ip, ipElement); + } + ipElement.addChild(serviceElement); + }else{ + const dummyElement = this.toDependencyElement(ElementType.USER,new Date().getTime()); + dummyElement.name = "???.???.???.???"; + _rootMap.put(dummyElement.id, dummyElement); + } + } + }); + console.log(_rootMap); + }catch (e) { + console.log(e); + } + }) } loadByGxId(){ @@ -399,9 +458,7 @@ class XlogFlow extends Component { //- render render() { - const {data} = this.state; - return(
@@ -416,6 +473,7 @@ class XlogFlow extends Component { ); } } + const mapStateToProps = (state) => { return { config: state.config, From b381b3a07e4f832a543193875ca8d61cd4b385c4 Mon Sep 17 00:00:00 2001 From: "yeosong.heo" Date: Wed, 16 Oct 2019 05:48:09 +0900 Subject: [PATCH 34/77] =?UTF-8?q?:construction:=20xlogflow=20=EB=82=B4?= =?UTF-8?q?=EB=B6=80=20=EC=BD=94=EB=93=9C=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FrameProfile/XlogFlow/XlogFlow.js | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js index 1d2e4e3..5882416 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js @@ -87,7 +87,8 @@ const Steps = { case this.SOCKET_SUM : return "SOCKET_SUM"; case this.APICALL_SUM : return "APICALL_SUM"; case this.CONTROL : return "CONTROL"; - + default : + return "UNKNOWN"; } }, toElapsedTime(step){ @@ -153,10 +154,10 @@ class XlogFlow extends Component { data: [] }; - constructor(props) { - super(props); - - }; + // constructor(props) { + // super(props); + // + // }; componentDidMount() { // console.log("componentDidMount"); //- first create event @@ -169,7 +170,7 @@ class XlogFlow extends Component { } componentWillReceiveProps(nextProps){ - console.log("componentWillReceiveProps"); + // console.log("componentWillReceiveProps"); } resize = () =>{ @@ -285,6 +286,8 @@ class XlogFlow extends Component { // case Steps.THREAD_SUBMIT: // -analys // break; + default : + } } flowOrder(globalTracing,serviceMap,objects){ @@ -300,6 +303,10 @@ class XlogFlow extends Component { excludeObjName = true; eType = ElementType.THREAD; break; + default: + excludeObjName=false; + eType = ElementType.SERVICE; + } const serviceElement = new DependencyElement({type:eType , id : _global.txid}); if(excludeObjName) { @@ -311,7 +318,7 @@ class XlogFlow extends Component { serviceElement.name = `${_global.service}\n(" ${_name} ")`; } - serviceElement.elapsed = new Number(_global.elapsed); + serviceElement.elapsed = Number(_global.elapsed); serviceElement.error = _global.error; serviceElement.threadName = _global.threadName; serviceElement.xtype = _global.xlogType; From cc042b5d36a3f8435417671284335d2fe2c12d52 Mon Sep 17 00:00:00 2001 From: kranian Date: Wed, 16 Oct 2019 19:23:02 +0900 Subject: [PATCH 35/77] :construction: tx flow painting prepare --- src/common/ElementType.js | 25 ++ .../Profiler/FrameProfile/FrameProfile.css | 4 +- .../XlogFlow/DependencyElement.js | 34 -- .../FrameProfile/XlogFlow/FlowElement.js | 77 ++++ .../FrameProfile/XlogFlow/XlogFlow.css | 2 +- .../FrameProfile/XlogFlow/XlogFlow.js | 156 +++---- .../FrameProfile/XlogFlow/XlogFlow.styl | 3 +- .../XlogFlow/XlogFlowChart/XlogFlowChart.js | 1 - .../XlogFlow/XlogFlowChart/XlogFlowChart.styl | 0 .../XlogFlow/XlogFlowChart/XlogFlowGraph.css | 13 + .../XlogFlow/XlogFlowChart/XlogFlowGraph.js | 408 ++++++++++++++++++ .../XlogFlow/XlogFlowChart/XlogFlowGraph.styl | 12 + .../XlogFlow/XlogFlowChart/XlogFlowRender.js | 48 --- 13 files changed, 618 insertions(+), 165 deletions(-) create mode 100644 src/common/ElementType.js delete mode 100644 src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/DependencyElement.js create mode 100644 src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js create mode 100644 src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowChart.styl create mode 100644 src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.css create mode 100644 src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js create mode 100644 src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.styl delete mode 100644 src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowRender.js diff --git a/src/common/ElementType.js b/src/common/ElementType.js new file mode 100644 index 0000000..1b64a96 --- /dev/null +++ b/src/common/ElementType.js @@ -0,0 +1,25 @@ +export default class ElementType { + + static defaultProps = { + USER : "0", + SERVICE : "1", + API_CALL : "2", + SQL : "3", + DISPATCH : "4", + THREAD : "5", + + toString(value) { + switch(value){ + case this.USER : return "USER"; + case this.SERVICE : return "SERVICE"; + case this.API_CALL : return "API_CALL"; + case this.SQL : return "SQL"; + case this.DISPATCH : return "DISPATCH"; + case this.THREAD : return "THREAD"; + default: + return "UNKNOWN"; + } + } + }; +} + diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.css b/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.css index e9aadd0..8b6167f 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.css +++ b/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.css @@ -471,7 +471,7 @@ display: table-cell; width: 100%; height: 100%; - vertical-align: bottom; + vertical-align: middle; padding: 0 10px; position: relative; box-sizing: border-box; @@ -479,6 +479,6 @@ .frame-profile .frame-xlog-flow > div > div { background-color: white; - height: calc(100% - 338px); + height: calc(100% - 120px); width: 100%; } \ No newline at end of file diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/DependencyElement.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/DependencyElement.js deleted file mode 100644 index 8feeb5e..0000000 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/DependencyElement.js +++ /dev/null @@ -1,34 +0,0 @@ -export default class DependencyElement { - name = null; - elapsed=0; - error=""; - xtype=""; - address=""; - threadName=""; -//- - type; - id; - dupleCnt = 1; -//-- add - children; - tags = {}; - constructor(obj={type:"0", id: "-1"}){ - this.id = obj.id; - this.type = obj.type; - this.children = new Map(); - }; - - addChild(child){ - const childObj = this.children.get(child.id); - if(childObj){ - childObj.dupleCnt += child.dupleCnt; - childObj.elapsed += child.elapsed; - if (!child.error) { - childObj.error = child.error; - } - }else{ - this.children.set(child.id,child); - } - } - -} \ No newline at end of file diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js new file mode 100644 index 0000000..2b027a2 --- /dev/null +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js @@ -0,0 +1,77 @@ +import ElementType from "../../../../../../common/ElementType.js"; + +export default class FlowElement { + name = null; + objName = null; + excludeObjName = false; + elapsed=0; + error=""; + xtype=""; + address=""; + threadName=""; + parent=null; +//- + type; + id; + dupleCnt = 1; +//-- add + children; + tags = {}; + constructor(obj={type:"0", id: "-1"}){ + this.id = obj.id; + this.type = obj.type; + this.children = new Map(); + }; + + addChild(child){ + const childObj = this.children.get(child.id); + if(childObj){ + childObj.dupleCnt += child.dupleCnt; + childObj.elapsed += child.elapsed; + if (!child.error) { + childObj.error = child.error; + } + }else{ + child.parent = this.id; + this.children.set(child.id,child); + } + } + toArray(){ + const ret = []; + for(const value of this.children.values()){ + ret.push(value); + } + return ret; + } + typeToname(){ + switch(this.type){ + case ElementType.defaultProps.SQL: + case ElementType.defaultProps.API_CALL: + return this.name; + case ElementType.defaultProps.DISPATCH: + case ElementType.defaultProps.THREAD: + return this.threadName ? `${this.name} \n < ${this.threadName} >` : this.name; + default : + return this.address ? `${this.name} \n : ${this.address}` : this.name; + } + } + toTree(){ + const ret = {}; + + ret["name"] = this.name; + ret["objName"] = this.objName; + ret["excludeObjName"] = this.excludeObjName; + ret["threadName"] = this.threadName; + ret["address"] = this.address; + ret["type"] = this.type; + ret["elapsed"] = this.elapsed; + ret["children"] = []; + ret["isError"] = this.error ? true : false; + + for(const value of this.children.values()){ + ret["children"].push(value.toTree()); + } + return ret; + } + +} \ No newline at end of file diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.css b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.css index e32d2bf..04b19a8 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.css +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.css @@ -19,7 +19,7 @@ display: inline-block !important; } .xlog-flow .flow-content { + width: 100%; height: 100%; overflow-y: auto; - width: 100%; } diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js index 5882416..336bfe3 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js @@ -4,10 +4,11 @@ import './XlogFlow.css' import {connect} from 'react-redux'; import {withRouter} from 'react-router-dom'; import XlogFlowChart from './XlogFlowChart/XlogFlowChart' -import XlogFlowRender from './XlogFlowChart/XlogFlowRender' -import DependencyElement from "./DependencyElement"; +import XlogFlowGraph from './XlogFlowChart/XlogFlowGraph' +import FlowElement from "./FlowElement"; import {getCurrentUser, getHttpProtocol, getWithCredentials, setAuthHeader} from "../../../../../../common/common"; +import ElementType from "../../../../../../common/ElementType"; import {addRequest, setControlVisibility} from "../../../../../../actions"; import * as _ from "lodash"; import moment from "moment/moment"; @@ -126,32 +127,12 @@ const Steps = { } }; -const ElementType = { - USER : "0", - SERVICE : "1", - API_CALL : "2", - SQL : "3", - DISPATCH : "4", - THREAD : "5", - - toString(value) { - switch(value){ - case this.USER : return "USER"; - case this.SERVICE : return "SERVICE"; - case this.API_CALL : return "API_CALL"; - case this.SQL : return "SQL"; - case this.DISPATCH : return "DISPATCH"; - case this.THREAD : return "THREAD"; - default: - return "UNKNOWN"; - } - } -}; class XlogFlow extends Component { state = { - data: [] + data : null, + dimensions : null }; // constructor(props) { @@ -163,6 +144,7 @@ class XlogFlow extends Component { //- first create event window.addEventListener("resize", this.resize); this.loadByGxId(); + } componentWillUnmount(){ @@ -174,13 +156,18 @@ class XlogFlow extends Component { } resize = () =>{ - + // this.setState({ + // dimensions: { + // width: this.container.offsetWidth, + // height: this.container.offsetHeight, + // }, + // }); }; stringTruncate(str,len){ return !str || str.length <= len ? str : str.substring(0, len); } - toDependencyElement(type,id){ - return new DependencyElement({type : type , id : id}); + FlowElement(type,id){ + return new FlowElement({type : type , id : id}); } stepToElement(serviceMap,thisElement,steps){ const {stepType} = steps.step; @@ -188,7 +175,7 @@ class XlogFlow extends Component { switch(stepType){ case Steps.APICALL: case Steps.APICALL2: - const apiElement = this.toDependencyElement(ElementType.API_CALL, step.txid + step.hash); + const apiElement = this.FlowElement(ElementType.defaultProps.API_CALL, step.txid + step.hash); apiElement.elapsed= Steps.toElapsedTime(step); apiElement.error = step.error; apiElement.name = mainValue; @@ -207,7 +194,7 @@ class XlogFlow extends Component { } break; case Steps.SPANCALL: - const spanCallElement = this.toDependencyElement(ElementType.API_CALL, step.txid + step.hash); + const spanCallElement = this.FlowElement(ElementType.defaultProps.API_CALL, step.txid + step.hash); spanCallElement.elapsed = Steps.toElapsedTime(step); spanCallElement.error = step.error; spanCallElement.name = mainValue; @@ -225,10 +212,10 @@ class XlogFlow extends Component { } break; case Steps.DISPATCH: - const dispatchElement = this.toDependencyElement(ElementType.DISPATCH, step.txid + step.hash); + const dispatchElement = this.FlowElement(ElementType.defaultProps.DISPATCH, step.txid + step.hash); dispatchElement.elapsed = Steps.toElapsedTime(step); dispatchElement.error = step.error; - dispatchElement.name = mainValue + dispatchElement.name = mainValue; if (step.txid !== "0") { const callElement = serviceMap.get(step.txid); if (callElement) { @@ -240,24 +227,24 @@ class XlogFlow extends Component { thisElement.addChild(dispatchElement); } break; - case Steps.THREAD_CALL_POSSIBLE: - //- other thread call checking - const tcElement = this.toDependencyElement(ElementType.DISPATCH, step.txid + step.hash); - tcElement.elapsed = Steps.toElapsedTime(step); - tcElement.name = mainValue; - if (step.txid !== "0") { - const callElement = serviceMap.get(step.txid); - if (callElement) { - thisElement.addChild(callElement.serviceElement); - } else { - thisElement.addChild(tcElement); - } - } else { - thisElement.addChild(tcElement); - } - break; + // case Steps.THREAD_CALL_POSSIBLE: + // //- other thread call checking + // const tcElement = this.FlowElement(ElementType.defaultProps.DISPATCH, step.txid + step.hash); + // tcElement.elapsed = Steps.toElapsedTime(step); + // tcElement.name = mainValue; + // if (step.txid !== "0") { + // const callElement = serviceMap.get(step.txid); + // if (callElement) { + // thisElement.addChild(callElement.serviceElement); + // } else { + // thisElement.addChild(tcElement); + // } + // } else { + // thisElement.addChild(tcElement); + // } + // break; case Steps.APICALL_SUM: - const apiSumElement = this.toDependencyElement(ElementType.API_CALL, step.hash); + const apiSumElement = this.FlowElement(ElementType.defaultProps.API_CALL, step.hash); apiSumElement.dupleCnt = step.count; apiSumElement.elapsed = Steps.toElapsedTime(step); apiSumElement.error = step.error; @@ -267,7 +254,7 @@ class XlogFlow extends Component { case Steps.SQL: case Steps.SQL2: case Steps.SQL3: - const sqlElement = this.toDependencyElement(ElementType.SQL, step.hash); + const sqlElement = this.FlowElement(ElementType.defaultProps.SQL, step.hash); sqlElement.elapsed = Steps.toElapsedTime(step); sqlElement.name = `${this.stringTruncate(mainValue, 20)}...`; sqlElement.error = step.error; @@ -275,7 +262,7 @@ class XlogFlow extends Component { thisElement.addChild(sqlElement); break; case Steps.SQL_SUM: - const sqlSumElement = this.toDependencyElement(ElementType.SQL, step.hash); + const sqlSumElement = this.FlowElement(ElementType.defaultProps.SQL, step.hash); sqlSumElement.dupleCnt = step.count; sqlSumElement.elapsed = Steps.toElapsedTime(step); sqlSumElement.error = step.error; @@ -293,35 +280,31 @@ class XlogFlow extends Component { flowOrder(globalTracing,serviceMap,objects){ return globalTracing.map(_global => { let excludeObjName = false; - let eType = ElementType.SERVICE; + let eType = null; switch(_global.xlogType){ - case XLogTypes.WEB_SERVICE: - eType = ElementType.DISPATCH; + case XLogTypes.ASYNCSERVLET_DISPATCHED_SERVICE: + eType = ElementType.defaultProps.DISPATCH; excludeObjName = true; break; case XLogTypes.BACK_THREAD2: excludeObjName = true; - eType = ElementType.THREAD; + eType = ElementType.defaultProps.THREAD; break; default: excludeObjName=false; - eType = ElementType.SERVICE; + eType = ElementType.defaultProps.SERVICE; } - const serviceElement = new DependencyElement({type:eType , id : _global.txid}); - if(excludeObjName) { - serviceElement.name = _global.service; - } else { - const _object = objects.get(_global.objHash); + const serviceElement = this.FlowElement(eType , _global.txid); + serviceElement.name = _global.service; + const _object = objects.get(_global.objHash); - const _name = _object.objName ? _object.objName : 'unknown'; - serviceElement.name = `${_global.service}\n(" ${_name} ")`; - } - - serviceElement.elapsed = Number(_global.elapsed); - serviceElement.error = _global.error; - serviceElement.threadName = _global.threadName; - serviceElement.xtype = _global.xlogType; + serviceElement.objName = _object ? _object.objName : 'unknown'; + serviceElement.excludeObjName = excludeObjName; + serviceElement.elapsed = Number(_global.elapsed); + serviceElement.error = _global.error; + serviceElement.threadName = _global.threadName; + serviceElement.xtype = _global.xlogType; serviceElement.tags = { caller: _global.caller, ip: _global.ipAddr @@ -338,6 +321,7 @@ class XlogFlow extends Component { } tryDepFlowSearch(globalTracing){ //next try step + this.props.setControlVisibility("Loading", true); const {config, user} = this.props; const _allofTrace = globalTracing.map(_tx =>{ const yyyymmdd = moment(new Date(Number(_tx.endTime))).format("YYYYMMDD"); @@ -367,6 +351,7 @@ class XlogFlow extends Component { } }); }); + const _objects = new Map(); const _serviceMap = new Map(); const _stepMap = new Map(); @@ -402,23 +387,38 @@ class XlogFlow extends Component { if(ip){ let ipElement = _rootMap.get(ip); if(!ipElement){ - ipElement = this.toDependencyElement(ElementType.USER,ip); + ipElement = this.FlowElement(ElementType.defaultProps.USER,ip); ipElement.name = ip; _rootMap.set(ip, ipElement); } ipElement.addChild(serviceElement); }else{ - const dummyElement = this.toDependencyElement(ElementType.USER,new Date().getTime()); + const dummyElement = this.FlowElement(ElementType.defaultProps.USER,new Date().getTime()); dummyElement.name = "???.???.???.???"; _rootMap.put(dummyElement.id, dummyElement); } } }); - console.log(_rootMap); }catch (e) { console.log(e); } - }) + }).always(()=>{ + this.props.setControlVisibility("Loading", false); + if(_rootMap.size > 0){ + this.setState((preState,props)=>{ + if(!preState.data){ + return { + data : _rootMap, + dimensions: { + width: this.container.offsetWidth, + height: this.container.offsetHeight + } + } + } + }); + } + + }); } loadByGxId(){ @@ -446,9 +446,9 @@ class XlogFlow extends Component { }); }; shouldComponentUpdate(nextProps,nextState) { - if( nextProps.flow !== this.props.flow) { - return false; - } + // if( nextProps.flow !== this.props.flow) { + // return false; + // } return true; } @@ -465,15 +465,15 @@ class XlogFlow extends Component { //- render render() { - const {data} = this.state; + const {data,dimensions} = this.state; return(
-
+
this.container = el }> - +
diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.styl b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.styl index e897634..0b9bb15 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.styl +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.styl @@ -15,6 +15,7 @@ right : 5px display: inline-block !important .flow-content + width: 100% height: 100% overflow-y: auto - width: 100% + diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowChart.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowChart.js index 64f5a45..8ea4399 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowChart.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowChart.js @@ -1,7 +1,6 @@ import React from 'react' import connect from "react-redux/es/connect/connect"; import {withRouter} from "react-router-dom"; - function XlogFlowChart({ width, height, children }) { return ( diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowChart.styl b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowChart.styl new file mode 100644 index 0000000..e69de29 diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.css b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.css new file mode 100644 index 0000000..ada8b85 --- /dev/null +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.css @@ -0,0 +1,13 @@ +.node circle { + fill: #fff; + stroke: #4682b4; + stroke-width: 3px; +} +/*.node text {*/ + /*font: 12px sans-serif;*/ +/*}*/ +.link { + fill: none; + stroke: #ccc; + stroke-width: 2px; +} diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js new file mode 100644 index 0000000..91baf79 --- /dev/null +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js @@ -0,0 +1,408 @@ +import connect from "react-redux/es/connect/connect"; +import {withRouter} from "react-router-dom"; +import React,{Component} from "react"; +import * as d3 from "d3"; +import "./XlogFlowGraph.css"; + +class XlogFlowGraph extends Component { + + state = { + g: null, + }; + + defaultProps ={ + height: 500, + width: 400, + duration : 750 ,//ms, + margin: { + top: 15, right: 15, bottom: 25, left: 40 + }, + }; + + constructor(props){ + super(props); + this.treemap = d3.tree().size([this.defaultProps.height, this.defaultProps.width]); + } + + componentWillReceiveProps(nextProps){ + if (nextProps.dimensions !== this.props.dimensions) { + // this.renderflow(nextProps.xlogflow); + this.defaultProps.height = nextProps.dimensions.height; + this.defaultProps.width = nextProps.dimensions.width; + this.treemap = d3.tree().size([this.defaultProps.height, this.defaultProps.width]); + } + if (nextProps.xlogflow !== this.props.xlogflow) { + this.renderflow(nextProps.xlogflow); + } + + } + shouldComponentUpdate() { + // we will handle moving the nodes on our own with d3.js + // make React ignore this component + return false; + } + + onRef = (ref) => { + this.setState({ g : d3.select(ref) }, () => { + d3.select(ref).attr('transform', d => `translate(120, 0)`); + this.renderflow(this.props.xlogflow); + }); + }; + init(){ + this.treemap = d3.tree().size([this.defaultProps.height, this.defaultProps.width]); + this.root = d3.hierarchy(this.data, d => d.children); + this.root.x0 = this.height / 2; + this.root.y0 = 0; + this.topSlow = []; + this.topChild = []; + this.root.children.forEach((d)=>this.collapse(d)); + this.topSlowMax = this.topSlow.sort((a,b) => b - a)[0]; + this.topSlowMin = this.topSlow.sort((a,b) => b - a)[4]; + this.topChildMax = this.topChild.sort((a,b) => b - a)[0]; + this.topChildMin = this.topChild.sort((a,b) => b - a)[4]; + this.update(this.root); + } + update(source) { + const that = this; + let treeData = this.treemap(this.root); + let nodes = treeData.descendants(), + links = treeData.descendants().slice(1); + + nodes.forEach((d)=> d.y = d.depth * 140); + let i=0; + let node = this.state.g.selectAll('g.node') + .data(nodes, function(d) {return d.id || (d.id = ++i); }); + + let nodeEnter = node.enter().append('g') + .attr('class', 'node') + .attr('cursor', 'pointer') + .attr("transform", function(d) { + return "translate(" + source.y0 + "," + source.x0 + ")"; + }).on('mouseover', function(d, i) { + // that.tip.show(d, this); + // if(!that.timeUpdate) {return;} + // const _node = that.timeUpdate._groups[0].filter(group => group.__data__.id === (i+1)); + // if(_node.length){ + // that.timeTip.show(d, _node[0].children[1]); + // } + }) + .on('mouseout', function(d, i) { + // that.tip.hide(d, this); + // if(!that.timeUpdate) {return;} + // const _node = that.timeUpdate._groups[0].filter(group => group.__data__.id === (i+1)); + // if(_node.length){ + // that.timeTip.hide(d, _node[0].children[1]); + // } + }) + .on('click', function(d) { + d3.event.stopPropagation(); + // that.scope.handleSelectSpan(d); + }); + + nodeEnter.append('circle') + .attr('class', 'node') + .attr('r', 1e-6) + // .style("fill", d => d._children ? this.sequentialScale(this.list.indexOf(d.data.serviceCode)) : "#fff") + // .attr('stroke', d => this.sequentialScale(this.list.indexOf(d.data.serviceCode))) + .attr('stroke-width', 2.5) + + nodeEnter.append('text') + .attr('font-size', 11) + .attr("dy", "-0.5em") + .attr("x", function(d) { + return d.children || d._children ? -15 : 15; + }) + .attr("text-anchor", function(d) { + return d.children || d._children ? "end" : "start"; + }) + .text(d => d.data.name.length > 19 ? (d.data.isError ?'◉ ': '') + d.data.name.slice(0, 19) + '...' : (d.data.isError?'◉ ': '') + d.data.name) + .style("fill", d => !d.data.isError? '#3d444f': '#E54C17'); + nodeEnter.append('text') + .attr('class','node-text') + .attr("x", function(d) { + return d.children || d._children ? -15 : 15; + }) + .attr("dy", "1em") + .attr('fill', '#bbb') + .attr("text-anchor", function(d) { + return d.children || d._children ? "end" : "start"; + }) + .style('font-size', '10px') + .text( + d => + `${d.data.layer || ''}${ + d.data.component ? '-' + d.data.component : d.data.component || '' + }` + ); + nodeEnter + .append('rect') + .attr('rx', 1) + .attr('ry', 1) + .attr('height', 2) + .attr('width', 100) + .attr('x', function(d) { + return d.children || d._children ? "-110" : "10"; + }) + .attr('y', -1) + .style('fill', '#00000020'); + nodeEnter + .append('rect') + .attr('rx', 1) + .attr('ry', 1) + .attr('height', 2) + .attr('width', d => { + if (!d.data.endTime || !d.data.startTime) return 0; + return this.xScale(d.data.endTime- d.data.startTime) + 1 || 0; + }) + .attr('x', d => { + if (!d.data.endTime || !d.data.startTime) { return 0; } + if( d.children || d._children ) { + return -110 + this.xScale(d.data.startTime - this.min) + } + return 10 + this.xScale(d.data.startTime - this.min) + }) + .attr('y', -1) + .style( 'fill', d => this.sequentialScale(this.list.indexOf(d.data.serviceCode))); + var nodeUpdate = nodeEnter.merge(node); + this.nodeUpdate = nodeUpdate; + nodeUpdate.transition() + .duration(600) + .attr('transform', function(d) { + return 'translate(' + d.y + ',' + d.x + ')'; + }); + + // Update the node attributes and style + nodeUpdate.select('circle.node') + .attr('r', 5) + .style("fill", (d) => d._children ? this.sequentialScale(this.list.indexOf(d.data.serviceCode)) : "#fff" ) + .attr('cursor', 'pointer') + .on('click', d => { + d3.event.stopPropagation(); + click(d); + }); + + // Remove any exiting nodes + var nodeExit = node.exit().transition() + .duration(600) + .attr('transform', function(d) { + return 'translate(' + source.y + ',' + source.x + ')'; + }) + .remove(); + + nodeExit.select('circle') + .attr('r', 1e-6); + + nodeExit.select('text') + .style('fill-opacity', 1e-6); + + var link = this.svg.selectAll('path.tree-link') + .data(links, function(d) { return d.id; }) + .style('stroke-width', 1.5); + + var linkEnter = link.enter().insert('path', "g") + .attr("class", "tree-link") + .attr('d', function(d){ + var o = {x: source.x0, y: source.y0} + return diagonal(o, o) + }) + .style('stroke-width', 1.5); + + var linkUpdate = linkEnter.merge(link); + linkUpdate.transition() + .duration(600) + .attr('d', function(d){ return diagonal(d, d.parent) }); + + var linkExit = link.exit().transition() + .duration(600) + .attr('d', function(d) { + var o = {x: source.x, y: source.y} + return diagonal(o, o) + }) + .style('stroke-width', 1.5) + .remove(); + + nodes.forEach(function(d){ + d.x0 = d.x; + d.y0 = d.y; + }); + function diagonal(s, d) { + return `M ${s.y} ${s.x} + C ${(s.y + d.y) / 2} ${s.x}, ${(s.y + d.y) / 2} ${d.x}, + ${d.y} ${d.x}`; + } + function click(d) { + if (d.children) { + d._children = d.children; + d.children = null; + } else { + d.children = d._children; + d._children = null; + } + that.update(d); + } + } + collapse=(d) =>{ + if(d.children) { + let dur = d.elapsed; + d.children.forEach((_d) => dur -= _d.elapsed); + + d.dur = dur < 0 ? 0 : dur; + this.topSlow.push(dur); + this.topChild.push(d.children.length); + d.childrenLength = d.children.length; + d.children.forEach(_d=>this.collapse(_d)) + } + }; + + renderflow = (data) =>{ + + if(!data){ + return; + } + + for( const val of data.values()){ + const treeData = val.toTree(); + // let root = d3.hierarchy(treeData, (d) =>d.children); + // root.x0 = this.defaultProps.height / 2; + // root.y0 = 0; + // this.update(root); + } + + }; + + render() { + + return ( + + ) + }; + +} + + +const mapStateToProps = (state) => { + return { + config: state.config + }; +}; + +XlogFlowGraph = connect(mapStateToProps, undefined)(XlogFlowGraph); +export default withRouter(XlogFlowGraph); + +// +// update(source){ +// const treeData = this.treemap(source); +// +// // Compute the new tree layout. +// let nodes = treeData.descendants(),links = treeData.descendants().slice(1); +// +// // Normalize for fixed-depth. +// nodes.forEach(function(d){ d.y = d.depth * 180}); +// +// // ****************** Nodes section *************************** +// +// // Update the nodes... +// let i=0; +// let node = this.state.g.selectAll('g.node') +// .data(nodes, function(d) {return d.id || (d.id = ++i); }); +// +// // Enter any new modes at the parent's previous position. +// let nodeEnter = node.enter().append('g') +// .attr('class', 'node') +// .attr("transform", function(d) { +// return "translate(" + source.y0 + "," + source.x0 + ")"; +// }); +// // .on('click', click); +// +// // Add Circle for the nodes +// nodeEnter.append('circle') +// .attr('class', 'node') +// .attr('r', 1e-6) +// .style("fill", function(d) { +// return d._children ? "lightsteelblue" : "#fff"; +// }); +// +// // Add labels for the nodes +// nodeEnter.append('text') +// .attr("dy", ".35em") +// .attr("x", function(d) { +// return d.children || d._children ? -13 : 13; +// }) +// .attr("text-anchor", function(d) { +// return d.children || d._children ? "end" : "start"; +// }) +// .text(function(d) { return d.data.name; }); +// +// // UPDATE +// let nodeUpdate = nodeEnter.merge(node); +// +// // Transition to the proper position for the node +// nodeUpdate.transition() +// .duration(this.defaultProps.duration) +// .attr("transform", function(d) { +// return "translate(" + d.y + "," + d.x + ")"; +// }); +// +// // Update the node attributes and style +// nodeUpdate.select('circle.node') +// .attr('r', 10) +// .style("fill", function(d) { +// return d._children ? "lightsteelblue" : "#fff"; +// }) +// .attr('cursor', 'pointer'); +// +// +// // Remove any exiting nodes +// var nodeExit = node.exit().transition() +// .duration(this.defaultProps.duration) +// .attr("transform", function(d) { +// return "translate(" + source.y + "," + source.x + ")"; +// }) +// .remove(); +// +// // On exit reduce the node circles size to 0 +// nodeExit.select('circle') +// .attr('r', 1e-6); +// +// // On exit reduce the opacity of text labels +// nodeExit.select('text') +// .style('fill-opacity', 1e-6); +// +// // ****************** links section *************************** +// +// // Update the links... +// let link = this.state.g.selectAll('path.link') +// .data(links, function(d) { return d.id; }); +// +// // Enter any new links at the parent's previous position. +// let linkEnter = link.enter().insert('path', "g") +// .attr("class", "link") +// .attr('d', (d)=>{ +// const o = {x: source.x0, y: source.y0} +// return this.diagonal(o, o); +// }); +// +// // UPDATE +// let linkUpdate = linkEnter.merge(link); +// +// // Transition back to the parent element position +// linkUpdate.transition() +// .duration(this.defaultProps.duration) +// .attr('d', (d)=>this.diagonal(d, d.parent)); +// +// // Remove any exiting links +// let linkExit = link.exit().transition() +// .duration(this.defaultProps.duration) +// .attr('d', (d)=> { +// const o = {x: source.x, y: source.y}; +// return this.diagonal(o, o); +// }) +// .remove(); +// +// // Store the old positions for transition. +// nodes.forEach(function(d){ +// d.x0 = d.x; +// d.y0 = d.y; +// }); +// +// } \ No newline at end of file diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.styl b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.styl new file mode 100644 index 0000000..57b35ab --- /dev/null +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.styl @@ -0,0 +1,12 @@ +.node + & circle + fill: #fff + stroke: steelblue + stroke-width: 3px + & text + font: 12px sans-serif +.link + fill: none; + stroke: #ccc; + stroke-width: 2px; + diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowRender.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowRender.js deleted file mode 100644 index a1f68db..0000000 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowRender.js +++ /dev/null @@ -1,48 +0,0 @@ -import connect from "react-redux/es/connect/connect"; -import {withRouter} from "react-router-dom"; -import React,{Component} from "react"; -import * as d3 from "d3"; - -class XlogFlowRender extends Component { - state = { - g: null, - }; - - constructor(props) { - super(props); - - }; - - shouldComponentUpdate() { - // we will handle moving the nodes on our own with d3.js - // make React ignore this component - return false; - } - - onRef = (ref) => { - this.setState({ g : d3.select(ref) }, () => this.renderflow(this.props.data)) - }; - - renderflow = (data) =>{ - - }; - - - render() { - - return ( - - ) - }; - -} - - -const mapStateToProps = (state) => { - return { - config: state.config - }; -}; - -XlogFlowRender = connect(mapStateToProps, undefined)(XlogFlowRender); -export default withRouter(XlogFlowRender); \ No newline at end of file From 328e1bacc7704dac8eae528db049730ededa4ed7 Mon Sep 17 00:00:00 2001 From: "yeosong.heo" Date: Thu, 17 Oct 2019 02:33:50 +0900 Subject: [PATCH 36/77] :construction: xlog drawing update; --- src/common/ElementType.js | 21 ++- .../FrameProfile/XlogFlow/FlowElement.js | 25 ++- .../FrameProfile/XlogFlow/XlogFlow.js | 2 +- .../XlogFlow/XlogFlowChart/XlogFlowGraph.css | 13 +- .../XlogFlow/XlogFlowChart/XlogFlowGraph.js | 168 +++++++++--------- .../XlogFlow/XlogFlowChart/XlogFlowGraph.styl | 16 +- 6 files changed, 141 insertions(+), 104 deletions(-) diff --git a/src/common/ElementType.js b/src/common/ElementType.js index 1b64a96..e427cef 100644 --- a/src/common/ElementType.js +++ b/src/common/ElementType.js @@ -1,12 +1,12 @@ export default class ElementType { static defaultProps = { - USER : "0", - SERVICE : "1", + USER : "0", + SERVICE : "1", API_CALL : "2", - SQL : "3", + SQL : "3", DISPATCH : "4", - THREAD : "5", + THREAD : "5", toString(value) { switch(value){ @@ -20,6 +20,19 @@ export default class ElementType { return "UNKNOWN"; } } + , + toColor(value){ + switch(value){ + case this.USER : return "#bd2c00"; + case this.SERVICE : return "#6e40aa"; + case this.API_CALL : return "#417de0"; + case this.SQL : return "#1ac7c2"; + case this.DISPATCH : return "#c9510c"; + case this.THREAD : return "#ffdd00"; + default: + return "#003666"; + } + } }; } diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js index 2b027a2..f1a55d5 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js @@ -1,4 +1,5 @@ import ElementType from "../../../../../../common/ElementType.js"; +import * as _ from 'lodash'; export default class FlowElement { name = null; @@ -37,7 +38,7 @@ export default class FlowElement { } } toArray(){ - const ret = []; + const ret = [{id : this.id,elapsed : this.elapsed} ]; for(const value of this.children.values()){ ret.push(value); } @@ -65,13 +66,33 @@ export default class FlowElement { ret["address"] = this.address; ret["type"] = this.type; ret["elapsed"] = this.elapsed; + ret["txid"] = this.id; ret["children"] = []; - ret["isError"] = this.error ? true : false; + ret["isError"] = false; for(const value of this.children.values()){ ret["children"].push(value.toTree()); } return ret; } + toElaped(){ + const elaps = []; + for(const value of this.children.values()){ + elaps.push( + value.toArray() + .filter(_d => _d.elapsed > 0 ) + .map(_d => { + return { + id :_d.id, + dup : _d.elapsed + }}) + ); + } + const flatMap = _.flatMapDeep(elaps); + return { + min : _.minBy(flatMap,(_d) => _d.dup), + max : _.maxBy(flatMap,(_d) => _d.dup) + } + } } \ No newline at end of file diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js index 336bfe3..8afcc35 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js @@ -473,7 +473,7 @@ class XlogFlow extends Component {
this.container = el }> - +
diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.css b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.css index ada8b85..985b45e 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.css +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.css @@ -1,13 +1,8 @@ -.node circle { - fill: #fff; - stroke: #4682b4; - stroke-width: 3px; +.node text { + font: 12px sans-serif; } -/*.node text {*/ - /*font: 12px sans-serif;*/ -/*}*/ -.link { +.tree-link { fill: none; - stroke: #ccc; + stroke: rgba(0,0,0,0.1); stroke-width: 2px; } diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js index 91baf79..5b21b93 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js @@ -3,6 +3,7 @@ import {withRouter} from "react-router-dom"; import React,{Component} from "react"; import * as d3 from "d3"; import "./XlogFlowGraph.css"; +import ElementType from "../../../../../../../common/ElementType"; class XlogFlowGraph extends Component { @@ -19,20 +20,19 @@ class XlogFlowGraph extends Component { }, }; + constructor(props){ super(props); - this.treemap = d3.tree().size([this.defaultProps.height, this.defaultProps.width]); } componentWillReceiveProps(nextProps){ - if (nextProps.dimensions !== this.props.dimensions) { + if (nextProps.resize !== this.props.resize) { // this.renderflow(nextProps.xlogflow); - this.defaultProps.height = nextProps.dimensions.height; - this.defaultProps.width = nextProps.dimensions.width; - this.treemap = d3.tree().size([this.defaultProps.height, this.defaultProps.width]); - } - if (nextProps.xlogflow !== this.props.xlogflow) { - this.renderflow(nextProps.xlogflow); + this.defaultProps.height = nextProps.resize.height; + this.defaultProps.width = nextProps.resize.width; + this.init(this.getTreeData(nextProps.xlogflow)); + }else if (nextProps.xlogflow !== this.props.xlogflow) { + this.draw(this.getTreeData(nextProps.xlogflow)); } } @@ -42,32 +42,55 @@ class XlogFlowGraph extends Component { return false; } + getTreeData(data){ + const [[key,value]]= [...data]; + console.log('getTreeData=',value); + return { + elapsed : value.toElaped(), + tree : value.toTree() + } + } + onRef = (ref) => { this.setState({ g : d3.select(ref) }, () => { d3.select(ref).attr('transform', d => `translate(120, 0)`); - this.renderflow(this.props.xlogflow); + d3.zoom().on("zoom", () => ref.parent.attr("transform", d3.event.transform)); }); }; - init(){ - this.treemap = d3.tree().size([this.defaultProps.height, this.defaultProps.width]); - this.root = d3.hierarchy(this.data, d => d.children); - this.root.x0 = this.height / 2; + init(data){ + const {height,width} = this.defaultProps; + const {elapsed,tree} = data; + this.treemap = d3.tree().size([height, width]); + this.root = d3.hierarchy(tree, d => d.children); + this.root.x0 = height / 2; this.root.y0 = 0; + this.min = elapsed.min.dup; + this.max = elapsed.max.dup; + this.xScale = d3 + .scaleLinear() + .range([0, 100]) + .domain([0, this.max]); + this.topSlow = []; this.topChild = []; + this.root.children.forEach((d)=>this.collapse(d)); - this.topSlowMax = this.topSlow.sort((a,b) => b - a)[0]; - this.topSlowMin = this.topSlow.sort((a,b) => b - a)[4]; - this.topChildMax = this.topChild.sort((a,b) => b - a)[0]; - this.topChildMin = this.topChild.sort((a,b) => b - a)[4]; - this.update(this.root); + + // this.topSlowMax = this.topSlow.sort((a,b) => b - a)[0]; + // this.topSlowMin = this.topSlow.sort((a,b) => b - a)[4]; + // this.topChildMax = this.topChild.sort((a,b) => b - a)[0]; + // this.topChildMin = this.topChild.sort((a,b) => b - a)[4]; + this.draw(tree); } - update(source) { + draw(source) { const that = this; + // Assigns the x and y position for the nodes let treeData = this.treemap(this.root); + // Compute the new tree layout. let nodes = treeData.descendants(), links = treeData.descendants().slice(1); + // Normalize for fixed-depth. nodes.forEach((d)=> d.y = d.depth * 140); let i=0; let node = this.state.g.selectAll('g.node') @@ -77,7 +100,7 @@ class XlogFlowGraph extends Component { .attr('class', 'node') .attr('cursor', 'pointer') .attr("transform", function(d) { - return "translate(" + source.y0 + "," + source.x0 + ")"; + return `translate(${source.y0} ,${source.x0})`; }).on('mouseover', function(d, i) { // that.tip.show(d, this); // if(!that.timeUpdate) {return;} @@ -95,43 +118,37 @@ class XlogFlowGraph extends Component { // } }) .on('click', function(d) { - d3.event.stopPropagation(); + // d3.event.stopPropagation(); // that.scope.handleSelectSpan(d); }); nodeEnter.append('circle') .attr('class', 'node') .attr('r', 1e-6) - // .style("fill", d => d._children ? this.sequentialScale(this.list.indexOf(d.data.serviceCode)) : "#fff") - // .attr('stroke', d => this.sequentialScale(this.list.indexOf(d.data.serviceCode))) - .attr('stroke-width', 2.5) + .style("fill", d => d._children ? ElementType.defaultProps.toColor(d.data.type) : "#fff") + .attr('stroke', d => ElementType.defaultProps.toColor(d.data.type)) + .attr('stroke-width', 2.5); + // Add labels for the nodes nodeEnter.append('text') .attr('font-size', 11) .attr("dy", "-0.5em") - .attr("x", function(d) { - return d.children || d._children ? -15 : 15; - }) - .attr("text-anchor", function(d) { - return d.children || d._children ? "end" : "start"; - }) + .attr("x", (d) =>d.children || d._children ? -15 : 15) + .attr("text-anchor", (d)=>d.children || d._children ? "end" : "start") .text(d => d.data.name.length > 19 ? (d.data.isError ?'◉ ': '') + d.data.name.slice(0, 19) + '...' : (d.data.isError?'◉ ': '') + d.data.name) .style("fill", d => !d.data.isError? '#3d444f': '#E54C17'); + nodeEnter.append('text') .attr('class','node-text') - .attr("x", function(d) { - return d.children || d._children ? -15 : 15; - }) + .attr("x", (d) => d.children || d._children ? -15 : 15) .attr("dy", "1em") .attr('fill', '#bbb') - .attr("text-anchor", function(d) { - return d.children || d._children ? "end" : "start"; - }) + .attr("text-anchor",(d) => d.children || d._children ? "end" : "start") .style('font-size', '10px') .text( d => - `${d.data.layer || ''}${ - d.data.component ? '-' + d.data.component : d.data.component || '' + `${d.data.objName || ''}${ + d.data.address ? '-' + d.data.address : d.data.objName || '' }` ); nodeEnter @@ -150,23 +167,24 @@ class XlogFlowGraph extends Component { .attr('rx', 1) .attr('ry', 1) .attr('height', 2) - .attr('width', d => { - if (!d.data.endTime || !d.data.startTime) return 0; - return this.xScale(d.data.endTime- d.data.startTime) + 1 || 0; - }) + .attr('width', d => this.xScale(d.data.elapsed) + 1 || 0) .attr('x', d => { - if (!d.data.endTime || !d.data.startTime) { return 0; } if( d.children || d._children ) { - return -110 + this.xScale(d.data.startTime - this.min) + return -110 + this.xScale(d.data.elapsed) } - return 10 + this.xScale(d.data.startTime - this.min) + return 10 + this.xScale(d.data.elapsed) }) .attr('y', -1) - .style( 'fill', d => this.sequentialScale(this.list.indexOf(d.data.serviceCode))); - var nodeUpdate = nodeEnter.merge(node); - this.nodeUpdate = nodeUpdate; + .style('fill',d =>ElementType.defaultProps.toColor(d.data.type)); + + // .style( 'fill', d => this.sequentialScale(this.list.indexOf(d.data.serviceCode))); + // .style( 'fill', d => this.sequentialScale(this.list.indexOf(d.data.serviceCode))); + + // UPDATE + let nodeUpdate = nodeEnter.merge(node); + nodeUpdate.transition() - .duration(600) + .duration(this.defaultProps.duration) .attr('transform', function(d) { return 'translate(' + d.y + ',' + d.x + ')'; }); @@ -174,7 +192,9 @@ class XlogFlowGraph extends Component { // Update the node attributes and style nodeUpdate.select('circle.node') .attr('r', 5) - .style("fill", (d) => d._children ? this.sequentialScale(this.list.indexOf(d.data.serviceCode)) : "#fff" ) + .style("fill", (d) => { + return d._children ? ElementType.defaultProps.toColor(d.data.type) : "#fff" + } ) .attr('cursor', 'pointer') .on('click', d => { d3.event.stopPropagation(); @@ -182,38 +202,42 @@ class XlogFlowGraph extends Component { }); // Remove any exiting nodes - var nodeExit = node.exit().transition() - .duration(600) + let nodeExit = node.exit().transition() + .duration(this.defaultProps.duration) .attr('transform', function(d) { return 'translate(' + source.y + ',' + source.x + ')'; }) .remove(); + // On exit reduce the node circles size to 0 nodeExit.select('circle') .attr('r', 1e-6); - + // On exit reduce the opacity of text labels nodeExit.select('text') .style('fill-opacity', 1e-6); - var link = this.svg.selectAll('path.tree-link') + // ****************** links section *************************** + + // Update the links... + let link = this.state.g.selectAll('path.tree-link') .data(links, function(d) { return d.id; }) .style('stroke-width', 1.5); - var linkEnter = link.enter().insert('path', "g") + let linkEnter = link.enter().insert('path', "g") .attr("class", "tree-link") .attr('d', function(d){ - var o = {x: source.x0, y: source.y0} - return diagonal(o, o) + let o = {x: source.x0, y: source.y0} + return diagonal(o, o); }) .style('stroke-width', 1.5); - var linkUpdate = linkEnter.merge(link); + let linkUpdate = linkEnter.merge(link); linkUpdate.transition() - .duration(600) + .duration(this.defaultProps.duration) .attr('d', function(d){ return diagonal(d, d.parent) }); var linkExit = link.exit().transition() - .duration(600) + .duration(this.defaultProps.duration) .attr('d', function(d) { var o = {x: source.x, y: source.y} return diagonal(o, o) @@ -238,15 +262,15 @@ class XlogFlowGraph extends Component { d.children = d._children; d._children = null; } - that.update(d); + that.draw(d); } } collapse=(d) =>{ if(d.children) { let dur = d.elapsed; - d.children.forEach((_d) => dur -= _d.elapsed); - - d.dur = dur < 0 ? 0 : dur; + // d.children.forEach((_d) => dur -= _d.elapsed); + // + d.dur = d.elapsed; this.topSlow.push(dur); this.topChild.push(d.children.length); d.childrenLength = d.children.length; @@ -254,22 +278,6 @@ class XlogFlowGraph extends Component { } }; - renderflow = (data) =>{ - - if(!data){ - return; - } - - for( const val of data.values()){ - const treeData = val.toTree(); - // let root = d3.hierarchy(treeData, (d) =>d.children); - // root.x0 = this.defaultProps.height / 2; - // root.y0 = 0; - // this.update(root); - } - - }; - render() { return ( diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.styl b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.styl index 57b35ab..544d61e 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.styl +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.styl @@ -1,12 +1,12 @@ .node - & circle - fill: #fff - stroke: steelblue - stroke-width: 3px + //& circle + // fill: #fff + // stroke: steelblue + // stroke-width: 3px & text font: 12px sans-serif -.link - fill: none; - stroke: #ccc; - stroke-width: 2px; +.tree-link + fill: none + stroke: rgba(0,0,0,.1) + stroke-width: 2px From a2680bcfb523610dea90bdecddccbac01f367a2a Mon Sep 17 00:00:00 2001 From: kranian Date: Thu, 17 Oct 2019 19:47:26 +0900 Subject: [PATCH 37/77] :construction: tx flow drawing end --- http/xflow.http | 17 ++- package.json | 1 + .../Profiler/FrameProfile/FrameProfile.js | 7 +- .../FrameProfile/XlogFlow/FlowElement.js | 11 +- .../FrameProfile/XlogFlow/XlogFlow.css | 4 +- .../FrameProfile/XlogFlow/XlogFlow.js | 111 +++++++++++----- .../FrameProfile/XlogFlow/XlogFlow.styl | 4 +- .../XlogFlow/XlogFlowChart/XlogFlowChart.styl | 0 .../XlogFlow/XlogFlowChart/XlogFlowGraph.js | 125 +++++++++++------- .../XlogFlow/XlogFlowChart/XlogFlowGraph.styl | 1 - 10 files changed, 182 insertions(+), 99 deletions(-) delete mode 100644 src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowChart.styl diff --git a/http/xflow.http b/http/xflow.http index 009414a..934c4f0 100644 --- a/http/xflow.http +++ b/http/xflow.http @@ -1,4 +1,7 @@ -GET http://demo.scouterapm.com:6188/scouter/v1/xlog-data/20191015/gxid/899870199175109144 +// XLogDataController.java +// SingleXLogRequest +GET http://demo.scouterapm.com:6188/scouter/v1/xlog-data/{yyyymmdd}/{txid} +# ### #"result": [ # { @@ -38,7 +41,7 @@ GET http://demo.scouterapm.com:6188/scouter/v1/xlog-data/20191015/gxid/899870199 # "queuing2ndTime": "0", # "xlogType": "ZIPKIN_SPAN" # }, -GET http://demo.scouterapm.com:6188/scouter/v1/profile-data/20191015/899870199175109144 +GET http://demo.scouterapm.com:6188/scouter/v1/profile-data/20191017/6506185853545897725 ### #"result": [ # { @@ -57,4 +60,12 @@ GET http://demo.scouterapm.com:6188/scouter/v1/profile-data/20191015/89987019917 # "order": "0", # "stepTypeName": "HASHED_MESSAGE" # } -# }, \ No newline at end of file +# }, + +GET http://demo.scouterapm.com:6188/scouter/v1/xlog/20191017/374225541302635030 + +### + +GET http://demo.scouterapm.com:6188/scouter/v1/xlog-data/20191017/-854231684852475423 + +### \ No newline at end of file diff --git a/package.json b/package.json index 7e2a3de..e2941ae 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "css-loader": "0.28.7", "d3": "^4.12.0", "d3-scale-chromatic": "^1.3.3", + "d3-tip": "^0.9.1", "detect-browser": "^2.1.0", "dotenv": "4.0.0", "eslint": "^4.18.2", diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.js b/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.js index 0b29c95..9b15164 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.js @@ -435,7 +435,7 @@ class FrameProfile extends Component { return "N"; } }; - onClickTxToXFlow=(gxid,txid,caller,endtime) =>{ + onClickTxToXFlow=(gxid,txid,caller,endtime,clickId ) =>{ const yyyymmdd = moment(new Date(Number(endtime))).format("YYYYMMDD"); this.setState({ flow : { @@ -444,7 +444,8 @@ class FrameProfile extends Component { gxid : gxid, txid : txid, caller : caller, - yyyymmdd : yyyymmdd + yyyymmdd : yyyymmdd, + isGX : clickId === "Gxid" } } }); @@ -484,7 +485,7 @@ class FrameProfile extends Component { {k} { const { gxid,txid,caller,endTime } = this.props.profile; - this.onClickTxToXFlow(gxid,txid,caller,endTime); + this.onClickTxToXFlow(gxid,txid,caller,endTime,k); }}>{IdAbbr.abbr(this.props.profile[k.toLowerCase()])}
}) diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js index f1a55d5..36a503a 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js @@ -6,11 +6,12 @@ export default class FlowElement { objName = null; excludeObjName = false; elapsed=0; - error=""; + error="0"; xtype=""; address=""; threadName=""; parent=null; + endTime = 0; //- type; id; @@ -60,15 +61,15 @@ export default class FlowElement { const ret = {}; ret["name"] = this.name; - ret["objName"] = this.objName; + ret["objName"] = this.objName ? this.objName : ""; ret["excludeObjName"] = this.excludeObjName; - ret["threadName"] = this.threadName; - ret["address"] = this.address; + ret["threadName"] = this.threadName ? this.threadName : ""; + ret["address"] = this.address ? this.address : ""; ret["type"] = this.type; ret["elapsed"] = this.elapsed; ret["txid"] = this.id; ret["children"] = []; - ret["isError"] = false; + ret["isError"] = this.error !== "0" ? true : false; for(const value of this.children.values()){ ret["children"].push(value.toTree()); diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.css b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.css index 04b19a8..b79bf13 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.css +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.css @@ -20,6 +20,6 @@ } .xlog-flow .flow-content { width: 100%; - height: 100%; - overflow-y: auto; + height: calc(100% - 36px); + overflow-y: hidden; } diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js index 8afcc35..0126123 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js @@ -12,9 +12,11 @@ import ElementType from "../../../../../../common/ElementType"; import {addRequest, setControlVisibility} from "../../../../../../actions"; import * as _ from "lodash"; import moment from "moment/moment"; +import {IdAbbr} from "../../../../../../common/idAbbr"; // const url=`/scouter/v1/xlog-data/${yyyymmdd}/gxid/${gxid}`; +// # load Txid const XLogTypes= { WEB_SERVICE : "WEB_SERVICE", @@ -143,7 +145,7 @@ class XlogFlow extends Component { // console.log("componentDidMount"); //- first create event window.addEventListener("resize", this.resize); - this.loadByGxId(); + this.loadFlow(); } @@ -156,12 +158,14 @@ class XlogFlow extends Component { } resize = () =>{ - // this.setState({ - // dimensions: { - // width: this.container.offsetWidth, - // height: this.container.offsetHeight, - // }, - // }); + if(this.container) { + this.setState({ + dimensions: { + width: this.container.offsetWidth, + height: this.container.offsetHeight, + } + }); + } }; stringTruncate(str,len){ return !str || str.length <= len ? str : str.substring(0, len); @@ -227,22 +231,38 @@ class XlogFlow extends Component { thisElement.addChild(dispatchElement); } break; - // case Steps.THREAD_CALL_POSSIBLE: - // //- other thread call checking - // const tcElement = this.FlowElement(ElementType.defaultProps.DISPATCH, step.txid + step.hash); - // tcElement.elapsed = Steps.toElapsedTime(step); - // tcElement.name = mainValue; - // if (step.txid !== "0") { - // const callElement = serviceMap.get(step.txid); - // if (callElement) { - // thisElement.addChild(callElement.serviceElement); - // } else { - // thisElement.addChild(tcElement); - // } - // } else { - // thisElement.addChild(tcElement); - // } - // break; + case Steps.THREAD_CALL_POSSIBLE: + if(step.threaded === "0") break; + //- other thread call checking + + const yyyymmdd = moment(new Date(Number(thisElement.endTime))).format("YYYYMMDD"); + const _url = `${getHttpProtocol(this.props.config)}/scouter/v1/xlog/${yyyymmdd}/${step.txid}`; + jQuery.ajax({ + method: "GET", + async: false, + dataType: "json", + url: _url, + xhrFields: getWithCredentials(this.props.config), + beforeSend: (xhr)=>{ + setAuthHeader(xhr, this.props.config, getCurrentUser(this.props.config, this.props.user)); + } + }).done(data=>{ + step.elapsed = data.elapsed; + }); + const tcElement = this.FlowElement(ElementType.defaultProps.DISPATCH, step.txid + step.hash); + tcElement.elapsed = Steps.toElapsedTime(step); + tcElement.name = mainValue; + if (step.txid !== "0") { + const callElement = serviceMap.get(step.txid); + if (callElement) { + thisElement.addChild(callElement.serviceElement); + } else { + thisElement.addChild(tcElement); + } + } else { + thisElement.addChild(tcElement); + } + break; case Steps.APICALL_SUM: const apiSumElement = this.FlowElement(ElementType.defaultProps.API_CALL, step.hash); apiSumElement.dupleCnt = step.count; @@ -256,7 +276,7 @@ class XlogFlow extends Component { case Steps.SQL3: const sqlElement = this.FlowElement(ElementType.defaultProps.SQL, step.hash); sqlElement.elapsed = Steps.toElapsedTime(step); - sqlElement.name = `${this.stringTruncate(mainValue, 20)}...`; + sqlElement.name = mainValue; sqlElement.error = step.error; sqlElement.tags.sql = mainValue; thisElement.addChild(sqlElement); @@ -266,13 +286,30 @@ class XlogFlow extends Component { sqlSumElement.dupleCnt = step.count; sqlSumElement.elapsed = Steps.toElapsedTime(step); sqlSumElement.error = step.error; - sqlSumElement.name = `${this.stringTruncate(mainValue, 20)}...`; + sqlSumElement.name = mainValue; sqlSumElement.tags.sql=mainValue; thisElement.addChild(sqlSumElement); break; - // case Steps.THREAD_SUBMIT: - // -analys - // break; + case Steps.THREAD_SUBMIT: + const sub_yyyymmdd = moment(new Date(Number(thisElement.endTime))).format("YYYYMMDD"); + const sub_url = `${getHttpProtocol(this.props.config)}/scouter/v1/profile-data/${sub_yyyymmdd}/${step.txid}`; + try { + jQuery.ajax({ + method: "GET", + async: false, + dataType: "json", + url: sub_url, + xhrFields: getWithCredentials(this.props.config), + beforeSend: (xhr) => { + setAuthHeader(xhr, this.props.config, getCurrentUser(this.props.config, this.props.user)); + } + }).done(data => { + data.result.forEach(_step => this.stepToElement(serviceMap, thisElement, _step)); + }); + }catch (e) { + console.log(e); + } + break; default : } @@ -305,6 +342,7 @@ class XlogFlow extends Component { serviceElement.error = _global.error; serviceElement.threadName = _global.threadName; serviceElement.xtype = _global.xlogType; + serviceElement.endTime = _global.endTime; serviceElement.tags = { caller: _global.caller, ip: _global.ipAddr @@ -319,6 +357,7 @@ class XlogFlow extends Component { }; }); } + tryDepFlowSearch(globalTracing){ //next try step this.props.setControlVisibility("Loading", true); @@ -421,17 +460,18 @@ class XlogFlow extends Component { }); } - loadByGxId(){ + loadFlow(){ this.props.setControlVisibility("Loading", true); this.props.addRequest(); const {config, user,flow } = this.props; // console.log("loadByGxId",flow,user); - const _url = `${getHttpProtocol(config)}/scouter/v1/xlog-data/${flow.parameter.yyyymmdd}/gxid/${flow.parameter.gxid}`; + const _gx_url = `${getHttpProtocol(config)}/scouter/v1/xlog-data/${flow.parameter.yyyymmdd}/gxid/${flow.parameter.gxid}`; + const _tx_url = `${getHttpProtocol(config)}/scouter/v1/xlog-data/${flow.parameter.yyyymmdd}/${flow.parameter.txid}`; jQuery.ajax({ method: "GET", async: true, dataType: 'text', - url: _url, + url: flow.parameter.isGX ? ( flow.parameter.gxid !== "0" ? _gx_url : _tx_url ) : _tx_url, xhrFields: getWithCredentials(config), beforeSend: function (xhr) { setAuthHeader(xhr, config, getCurrentUser(config, user)); @@ -439,7 +479,8 @@ class XlogFlow extends Component { }).done((msg) => { const reponseObj = JSON.parse(msg); if(reponseObj.status === "200"){ - this.tryDepFlowSearch(reponseObj.result); + const isArray = Array.isArray(reponseObj.result); + this.tryDepFlowSearch(isArray ? reponseObj.result : [reponseObj.result]); } }).always(() => { this.props.setControlVisibility("Loading", false); @@ -466,9 +507,15 @@ class XlogFlow extends Component { //- render render() { const {data,dimensions} = this.state; + const {flow} = this.props; return(
+ SERVICE FLOW- { + flow.parameter.isGX ? (flow.parameter.gxid !== "0" ? IdAbbr.abbr(flow.parameter.gxid) : IdAbbr.abbr(flow.parameter.txid)) + : IdAbbr.abbr(flow.parameter.txid) + } +
this.container = el }> diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.styl b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.styl index 0b9bb15..0761cfe 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.styl +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.styl @@ -16,6 +16,6 @@ display: inline-block !important .flow-content width: 100% - height: 100% - overflow-y: auto + height: calc(100% - 36px) + overflow-y : hidden diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowChart.styl b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowChart.styl deleted file mode 100644 index e69de29..0000000 diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js index 5b21b93..c881a3a 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js @@ -2,9 +2,16 @@ import connect from "react-redux/es/connect/connect"; import {withRouter} from "react-router-dom"; import React,{Component} from "react"; import * as d3 from "d3"; +import d3tip from 'd3-tip'; import "./XlogFlowGraph.css"; import ElementType from "../../../../../../../common/ElementType"; + +// # zoom in, zoom out +// # resizing +// # positing +// # tooltip +// # full info class XlogFlowGraph extends Component { state = { @@ -19,18 +26,17 @@ class XlogFlowGraph extends Component { top: 15, right: 15, bottom: 25, left: 40 }, }; - - - constructor(props){ - super(props); - } + // + // + // constructor(props){ + // super(props); + // } componentWillReceiveProps(nextProps){ if (nextProps.resize !== this.props.resize) { - // this.renderflow(nextProps.xlogflow); this.defaultProps.height = nextProps.resize.height; this.defaultProps.width = nextProps.resize.width; - this.init(this.getTreeData(nextProps.xlogflow)); + this.init(this.getTreeData(nextProps.xlogflow)) }else if (nextProps.xlogflow !== this.props.xlogflow) { this.draw(this.getTreeData(nextProps.xlogflow)); } @@ -44,7 +50,6 @@ class XlogFlowGraph extends Component { getTreeData(data){ const [[key,value]]= [...data]; - console.log('getTreeData=',value); return { elapsed : value.toElaped(), tree : value.toTree() @@ -53,10 +58,53 @@ class XlogFlowGraph extends Component { onRef = (ref) => { this.setState({ g : d3.select(ref) }, () => { - d3.select(ref).attr('transform', d => `translate(120, 0)`); - d3.zoom().on("zoom", () => ref.parent.attr("transform", d3.event.transform)); + let zoom = d3.zoom() + .scaleExtent([0.7, 10]) + .on("zoom", this.zoomed); + + d3.select(ref) + .attr('transform', d => `translate(120, 0)`); + + d3.select(ref.parentNode) + .call(zoom); + // + this.tip = d3tip() + .attr('class', 'd3-tip') + .style('z-index', '999') + .offset([-8, 0]) + .html(d =>{ + let svc = ""; + switch(d.data.type){ + case ElementType.defaultProps.SERVICE: + svc=d.data.name; + break; + case ElementType.defaultProps.THREAD: + svc=d.data.name; + break; + default : + svc=""; + } + return [ + { key : 'svc', value : svc , dis : `${svc}` } + ,{ key : 'obj', value : d.data.objName , dis : `( ${d.data.objName} )` } + ,{ key : 'adr', value : d.data.address , dis : `: ${d.data.address} `} + ,{ key : 'the', value : d.data.threadName , dis : `: ${d.data.threadName} `} + ] + .filter(d=> d.value ? true: false) + .map(d=> `
${d.dis}
`).join('') + }); + d3.select(ref.parentNode) + .call(this.tip); + }); }; + zoomed = () =>{ + this.state.g.attr( + 'transform', + `translate(${d3.event.transform.x + 120},${d3.event.transform.y})scale(${d3.event.transform.k})` + ) + }; + init(data){ const {height,width} = this.defaultProps; const {elapsed,tree} = data; @@ -64,22 +112,17 @@ class XlogFlowGraph extends Component { this.root = d3.hierarchy(tree, d => d.children); this.root.x0 = height / 2; this.root.y0 = 0; - this.min = elapsed.min.dup; - this.max = elapsed.max.dup; + // this.min = elapsed.min ? elapsed.min.dup : 0; + this.max = elapsed.max ? elapsed.max.dup : 0; this.xScale = d3 .scaleLinear() .range([0, 100]) .domain([0, this.max]); - this.topSlow = []; - this.topChild = []; + this.topSlow=[]; + this.topChild=[]; this.root.children.forEach((d)=>this.collapse(d)); - - // this.topSlowMax = this.topSlow.sort((a,b) => b - a)[0]; - // this.topSlowMin = this.topSlow.sort((a,b) => b - a)[4]; - // this.topChildMax = this.topChild.sort((a,b) => b - a)[0]; - // this.topChildMin = this.topChild.sort((a,b) => b - a)[4]; this.draw(tree); } draw(source) { @@ -101,24 +144,14 @@ class XlogFlowGraph extends Component { .attr('cursor', 'pointer') .attr("transform", function(d) { return `translate(${source.y0} ,${source.x0})`; - }).on('mouseover', function(d, i) { - // that.tip.show(d, this); - // if(!that.timeUpdate) {return;} - // const _node = that.timeUpdate._groups[0].filter(group => group.__data__.id === (i+1)); - // if(_node.length){ - // that.timeTip.show(d, _node[0].children[1]); - // } + }).on('mouseover', function (d, i) { + that.tip.show(d,this); }) .on('mouseout', function(d, i) { - // that.tip.hide(d, this); - // if(!that.timeUpdate) {return;} - // const _node = that.timeUpdate._groups[0].filter(group => group.__data__.id === (i+1)); - // if(_node.length){ - // that.timeTip.hide(d, _node[0].children[1]); - // } + that.tip.hide(d,this); }) .on('click', function(d) { - // d3.event.stopPropagation(); + d3.event.stopPropagation(); // that.scope.handleSelectSpan(d); }); @@ -129,6 +162,7 @@ class XlogFlowGraph extends Component { .attr('stroke', d => ElementType.defaultProps.toColor(d.data.type)) .attr('stroke-width', 2.5); + // Add labels for the nodes nodeEnter.append('text') .attr('font-size', 11) @@ -142,24 +176,18 @@ class XlogFlowGraph extends Component { .attr('class','node-text') .attr("x", (d) => d.children || d._children ? -15 : 15) .attr("dy", "1em") - .attr('fill', '#bbb') + .attr('fill', (d) => ElementType.defaultProps.toColor(d.data.type)) .attr("text-anchor",(d) => d.children || d._children ? "end" : "start") .style('font-size', '10px') - .text( - d => - `${d.data.objName || ''}${ - d.data.address ? '-' + d.data.address : d.data.objName || '' - }` - ); + .text(d =>`${ElementType.defaultProps.toString(d.data.type)}-${d.data.elapsed}ms`); + nodeEnter .append('rect') .attr('rx', 1) .attr('ry', 1) .attr('height', 2) .attr('width', 100) - .attr('x', function(d) { - return d.children || d._children ? "-110" : "10"; - }) + .attr('x', (d) =>d.children ? "-110" : "10") .attr('y', -1) .style('fill', '#00000020'); nodeEnter @@ -168,12 +196,7 @@ class XlogFlowGraph extends Component { .attr('ry', 1) .attr('height', 2) .attr('width', d => this.xScale(d.data.elapsed) + 1 || 0) - .attr('x', d => { - if( d.children || d._children ) { - return -110 + this.xScale(d.data.elapsed) - } - return 10 + this.xScale(d.data.elapsed) - }) + .attr('x', (d) => d.children ? "-110" : "10") .attr('y', -1) .style('fill',d =>ElementType.defaultProps.toColor(d.data.type)); @@ -235,8 +258,8 @@ class XlogFlowGraph extends Component { linkUpdate.transition() .duration(this.defaultProps.duration) .attr('d', function(d){ return diagonal(d, d.parent) }); - - var linkExit = link.exit().transition() +//linkExit + link.exit().transition() .duration(this.defaultProps.duration) .attr('d', function(d) { var o = {x: source.x, y: source.y} diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.styl b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.styl index 544d61e..34bd5cb 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.styl +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.styl @@ -1,5 +1,4 @@ .node - //& circle // fill: #fff // stroke: steelblue // stroke-width: 3px From 3e85631f7b60022906ffb6dc1f5e8a5f61536b0a Mon Sep 17 00:00:00 2001 From: "yeosong.heo" Date: Fri, 18 Oct 2019 02:23:10 +0900 Subject: [PATCH 38/77] :construction: xlogflow content adding --- src/common/ElementType.js | 2 +- .../Profiler/FrameProfile/FrameProfile.css | 2 +- .../FrameProfile/XlogFlow/FlowElement.js | 5 +- .../FrameProfile/XlogFlow/XlogFlow.css | 28 ++++++++- .../FrameProfile/XlogFlow/XlogFlow.js | 42 ++++++++++++-- .../FrameProfile/XlogFlow/XlogFlow.styl | 26 ++++++++- .../XlogFlow/XlogFlowChart/XlogFlowGraph.css | 5 ++ .../XlogFlow/XlogFlowChart/XlogFlowGraph.js | 57 ++++++++++++------- .../XlogFlow/XlogFlowChart/XlogFlowGraph.styl | 5 +- .../XlogFlowContent/XlogFlowContent.css | 26 +++++++++ .../XlogFlowContent/XlogFlowContent.js | 34 +++++++++++ .../XlogFlowContent/XlogFlowContent.styl | 21 +++++++ 12 files changed, 217 insertions(+), 36 deletions(-) create mode 100644 src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.css create mode 100644 src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js create mode 100644 src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.styl diff --git a/src/common/ElementType.js b/src/common/ElementType.js index e427cef..9e4de2d 100644 --- a/src/common/ElementType.js +++ b/src/common/ElementType.js @@ -28,7 +28,7 @@ export default class ElementType { case this.API_CALL : return "#417de0"; case this.SQL : return "#1ac7c2"; case this.DISPATCH : return "#c9510c"; - case this.THREAD : return "#ffdd00"; + case this.THREAD : return "#76b852"; default: return "#003666"; } diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.css b/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.css index 8b6167f..a595cc7 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.css +++ b/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.css @@ -479,6 +479,6 @@ .frame-profile .frame-xlog-flow > div > div { background-color: white; - height: calc(100% - 120px); + height: calc(100% - 400px); width: 100%; } \ No newline at end of file diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js index 36a503a..b42e20d 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js @@ -59,17 +59,16 @@ export default class FlowElement { } toTree(){ const ret = {}; - ret["name"] = this.name; ret["objName"] = this.objName ? this.objName : ""; ret["excludeObjName"] = this.excludeObjName; ret["threadName"] = this.threadName ? this.threadName : ""; ret["address"] = this.address ? this.address : ""; ret["type"] = this.type; - ret["elapsed"] = this.elapsed; + ret["elapsed"] = isNaN(this.elapsed) ? 0 : this.elapsed; ret["txid"] = this.id; ret["children"] = []; - ret["isError"] = this.error !== "0" ? true : false; + ret["isError"] = this.error === "0" || this.error === "" ? false: true; for(const value of this.children.values()){ ret["children"].push(value.toTree()); diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.css b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.css index b79bf13..27cc5ae 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.css +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.css @@ -18,8 +18,34 @@ right: 5px; display: inline-block !important; } -.xlog-flow .flow-content { +.xlog-flow .contents { width: 100%; height: calc(100% - 36px); overflow-y: hidden; } +.xlog-flow .frame-xlog-flow-content { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0,0,0,0.6); + display: table; + width: 100%; + height: 100%; + z-index: 3; +} +.xlog-flow .frame-xlog-flow-content > div { + display: table-cell; + width: 100%; + height: 100%; + vertical-align: middle; + padding: 0 10px; + position: relative; + box-sizing: border-box; +} +.xlog-flow .frame-xlog-flow-content > div > div { + background-color: #fff; + height: calc(100% - 110px); + width: 100%; +} diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js index 0126123..b386c4f 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js @@ -5,8 +5,10 @@ import {connect} from 'react-redux'; import {withRouter} from 'react-router-dom'; import XlogFlowChart from './XlogFlowChart/XlogFlowChart' import XlogFlowGraph from './XlogFlowChart/XlogFlowGraph' +import XlogFlowContent from './XlogFlowContent/XlogFlowContent' import FlowElement from "./FlowElement"; + import {getCurrentUser, getHttpProtocol, getWithCredentials, setAuthHeader} from "../../../../../../common/common"; import ElementType from "../../../../../../common/ElementType"; import {addRequest, setControlVisibility} from "../../../../../../actions"; @@ -134,7 +136,11 @@ class XlogFlow extends Component { state = { data : null, - dimensions : null + dimensions : null, + flowContent: { + show : false, + data : null + } }; // constructor(props) { @@ -493,20 +499,36 @@ class XlogFlow extends Component { return true; } - + clickContent=(flowData)=>{ + this.setState({ + flowContent :{ + show : true, + data : flowData + } + }); + }; //-- event list + closeContent=() =>{ + this.setState({ + flowContent :{ + show : false, + data : null + } + }); + }; close= () =>{ this.props.close({ flow : { show : false, - parameter : {} + parameter : {}, + } }); }; //- render render() { - const {data,dimensions} = this.state; + const {data,dimensions,flowContent} = this.state; const {flow} = this.props; return(
@@ -518,11 +540,19 @@ class XlogFlow extends Component {
-
this.container = el }> +
this.container = el }> - +
+ { + flowContent.show && +
+
+ +
+
+ }
); } diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.styl b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.styl index 0761cfe..4b55165 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.styl +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.styl @@ -14,8 +14,30 @@ top : 7px right : 5px display: inline-block !important - .flow-content + .contents width: 100% height: calc(100% - 36px) overflow-y : hidden - + .frame-xlog-flow-content + position: absolute + top: 0 + left: 0 + right: 0 + bottom: 0 + background-color: rgba(0, 0, 0, 0.6) + display: table + width: 100% + height: 100% + z-index: 3 + & > div + display: table-cell + width: 100% + height: 100% + vertical-align: middle + padding: 0 10px + position: relative + box-sizing: border-box + & > div + background-color: white + height: calc(100% - 110px) + width: 100% \ No newline at end of file diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.css b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.css index 985b45e..2e935e1 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.css +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.css @@ -6,3 +6,8 @@ stroke: rgba(0,0,0,0.1); stroke-width: 2px; } +.error-link { + fill: none; + stroke: #ce1126; + stroke-width: 2px; +} diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js index c881a3a..0cf90d4 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js @@ -21,7 +21,7 @@ class XlogFlowGraph extends Component { defaultProps ={ height: 500, width: 400, - duration : 750 ,//ms, + duration : 500 ,//ms, margin: { top: 15, right: 15, bottom: 25, left: 40 }, @@ -38,7 +38,7 @@ class XlogFlowGraph extends Component { this.defaultProps.width = nextProps.resize.width; this.init(this.getTreeData(nextProps.xlogflow)) }else if (nextProps.xlogflow !== this.props.xlogflow) { - this.draw(this.getTreeData(nextProps.xlogflow)); + this.draw(this.getTreeData(nextProps.xlogflow)); } } @@ -49,7 +49,7 @@ class XlogFlowGraph extends Component { } getTreeData(data){ - const [[key,value]]= [...data]; + const [[,value]]= [...data]; return { elapsed : value.toElaped(), tree : value.toTree() @@ -75,24 +75,22 @@ class XlogFlowGraph extends Component { .html(d =>{ let svc = ""; switch(d.data.type){ - case ElementType.defaultProps.SERVICE: - svc=d.data.name; - break; - case ElementType.defaultProps.THREAD: - svc=d.data.name; + case ElementType.defaultProps.SQL: + svc=""; break; default : - svc=""; + svc=d.data.name; } return [ - { key : 'svc', value : svc , dis : `${svc}` } - ,{ key : 'obj', value : d.data.objName , dis : `( ${d.data.objName} )` } - ,{ key : 'adr', value : d.data.address , dis : `: ${d.data.address} `} - ,{ key : 'the', value : d.data.threadName , dis : `: ${d.data.threadName} `} + { key : 'svc', value : svc , dis : `${svc}` } + ,{ key : 'obj', value : d.data.objName , dis : `${d.data.objName}` } + ,{ key : 'adr', value : d.data.address , dis : `${d.data.address} `} + ,{ key : 'the', value : d.data.threadName , dis : `${d.data.threadName}`} ] .filter(d=> d.value ? true: false) .map(d=> `
${d.dis}
`).join('') }); + d3.select(ref.parentNode) d3.select(ref.parentNode) .call(this.tip); @@ -150,15 +148,16 @@ class XlogFlowGraph extends Component { .on('mouseout', function(d, i) { that.tip.hide(d,this); }) - .on('click', function(d) { + .on('click', (d)=> { d3.event.stopPropagation(); + this.props.clickContent(d.data); // that.scope.handleSelectSpan(d); }); nodeEnter.append('circle') .attr('class', 'node') .attr('r', 1e-6) - .style("fill", d => d._children ? ElementType.defaultProps.toColor(d.data.type) : "#fff") + .style("fill", d => d.children || d._children ? ElementType.defaultProps.toColor(d.data.type) : "#fff") .attr('stroke', d => ElementType.defaultProps.toColor(d.data.type)) .attr('stroke-width', 2.5); @@ -169,8 +168,24 @@ class XlogFlowGraph extends Component { .attr("dy", "-0.5em") .attr("x", (d) =>d.children || d._children ? -15 : 15) .attr("text-anchor", (d)=>d.children || d._children ? "end" : "start") - .text(d => d.data.name.length > 19 ? (d.data.isError ?'◉ ': '') + d.data.name.slice(0, 19) + '...' : (d.data.isError?'◉ ': '') + d.data.name) - .style("fill", d => !d.data.isError? '#3d444f': '#E54C17'); + .text(d => { + const {txid,isError,name} = d.data; + const isOwner = txid === this.props.txid; + if( name.length > 19 ){ + return `${isOwner ? '▶ ' : (isError ? '◉' : '')}${name.slice(0, 19)}...`; + }else { + return `${isOwner ? '▶ ' : (isError ? '◉' : '')}${name}`; + } + }) + .style("fill", d => { + const {txid,isError} = d.data; + const isOwner = txid === this.props.txid; + if( isOwner ){ + return '#2529d8'; + }else{ + return !isError ? '#3d444f':'#E54C17'; + } + }); nodeEnter.append('text') .attr('class','node-text') @@ -179,7 +194,7 @@ class XlogFlowGraph extends Component { .attr('fill', (d) => ElementType.defaultProps.toColor(d.data.type)) .attr("text-anchor",(d) => d.children || d._children ? "end" : "start") .style('font-size', '10px') - .text(d =>`${ElementType.defaultProps.toString(d.data.type)}-${d.data.elapsed}ms`); + .text(d =>`${ElementType.defaultProps.toString(d.data.type)}`+ (d.data.type !== '0' ? `-${d.data.elapsed}ms` : '') ); nodeEnter .append('rect') @@ -196,7 +211,7 @@ class XlogFlowGraph extends Component { .attr('ry', 1) .attr('height', 2) .attr('width', d => this.xScale(d.data.elapsed) + 1 || 0) - .attr('x', (d) => d.children ? "-110" : "10") + .attr('x', (d) => d.children || d._children ? "-110" : "10") .attr('y', -1) .style('fill',d =>ElementType.defaultProps.toColor(d.data.type)); @@ -216,7 +231,7 @@ class XlogFlowGraph extends Component { nodeUpdate.select('circle.node') .attr('r', 5) .style("fill", (d) => { - return d._children ? ElementType.defaultProps.toColor(d.data.type) : "#fff" + return d.children || d._children ? ElementType.defaultProps.toColor(d.data.type) : "#fff" } ) .attr('cursor', 'pointer') .on('click', d => { @@ -247,7 +262,7 @@ class XlogFlowGraph extends Component { .style('stroke-width', 1.5); let linkEnter = link.enter().insert('path', "g") - .attr("class", "tree-link") + .attr("class",(d)=>`tree-link ${d.data.isError ? 'error-link' : ''}`) .attr('d', function(d){ let o = {x: source.x0, y: source.y0} return diagonal(o, o); diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.styl b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.styl index 34bd5cb..643862f 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.styl +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.styl @@ -8,4 +8,7 @@ fill: none stroke: rgba(0,0,0,.1) stroke-width: 2px - +.error-link + fill: none + stroke: #ce1126 + stroke-width: 2px diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.css b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.css new file mode 100644 index 0000000..25014b9 --- /dev/null +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.css @@ -0,0 +1,26 @@ +.xlog-flow-content { + position: relative; +} +.xlog-flow-content .title { + padding: 8px; + background-color: #333; + color: #fff; + height: 36px; + box-sizing: border-box; +} +.xlog-flow-content .title span { + display: inline-block; + padding: 4px; +} +.xlog-flow-content .close-btn { + position: absolute; + top: 7px; + right: 5px; + display: inline-block !important; +} +.xlog-flow-content .contents { + position: relative; + width: 100%; + height: calc(100% - 36px); + overflow-y: auto; +} diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js new file mode 100644 index 0000000..fd49821 --- /dev/null +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js @@ -0,0 +1,34 @@ +import React,{Component} from "react"; +import {withRouter} from "react-router-dom"; +import {connect} from "react-redux"; +import "./XlogFlowContent.css" +import {IdAbbr} from "../../../../../../../common/idAbbr"; +class XlogFlowContent extends Component { + + close=()=>{ + this.props.close(); + }; + + render(){ + return ( +
+
+ FLOW CONTENTS - {IdAbbr.abbr(this.props.content.txid)} +
+
+
+
+                        {this.props.content.name}
+                    
+
+
+ ) + } +} +const mapStateToProps = (state) => { + return { + config: state.config, + }; +}; +XlogFlowContent = connect(mapStateToProps, null)(XlogFlowContent); +export default withRouter(XlogFlowContent); \ No newline at end of file diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.styl b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.styl new file mode 100644 index 0000000..edbba1d --- /dev/null +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.styl @@ -0,0 +1,21 @@ +.xlog-flow-content + position: relative + .title + padding: 8px + background-color: #333 + color: white + height: 36px + box-sizing: border-box + & span + display: inline-block + padding: 4px + .close-btn + position: absolute + top : 7px + right : 5px + display: inline-block !important + .contents + position: relative + width: 100% + height: calc(100% - 36px) + overflow-y : auto From 4945e6deb61a703e98e17da6685b6c1522133093 Mon Sep 17 00:00:00 2001 From: kranian Date: Fri, 18 Oct 2019 15:37:18 +0900 Subject: [PATCH 39/77] tx flow click button adding tx flow contents viewer #218 --- src/common/ElementType.js | 2 +- .../Profiler/FrameProfile/FrameProfile.js | 2 +- .../FrameProfile/XlogFlow/FlowElement.js | 7 +- .../FrameProfile/XlogFlow/XlogFlow.css | 2 +- .../FrameProfile/XlogFlow/XlogFlow.js | 10 +- .../FrameProfile/XlogFlow/XlogFlow.styl | 2 +- .../XlogFlow/XlogFlowChart/XlogFlowChart.js | 21 ++- .../XlogFlow/XlogFlowChart/XlogFlowGraph.js | 135 ++--------------- .../XlogFlowContent/XlogFlowContent.css | 32 ++++ .../XlogFlowContent/XlogFlowContent.js | 138 +++++++++++++++++- .../XlogFlowContent/XlogFlowContent.styl | 38 +++++ 11 files changed, 244 insertions(+), 145 deletions(-) diff --git a/src/common/ElementType.js b/src/common/ElementType.js index 9e4de2d..aaf8f46 100644 --- a/src/common/ElementType.js +++ b/src/common/ElementType.js @@ -23,7 +23,7 @@ export default class ElementType { , toColor(value){ switch(value){ - case this.USER : return "#bd2c00"; + case this.USER : return "#aeaeae"; case this.SERVICE : return "#6e40aa"; case this.API_CALL : return "#417de0"; case this.SQL : return "#1ac7c2"; diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.js b/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.js index 9b15164..2a1e07b 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.js @@ -616,7 +616,7 @@ class FrameProfile extends Component { this.state.flow.show &&
- +
diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js index b42e20d..aa32ac5 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js @@ -28,9 +28,9 @@ export default class FlowElement { addChild(child){ const childObj = this.children.get(child.id); if(childObj){ - childObj.dupleCnt += child.dupleCnt; + childObj.dupleCnt += Number(child.dupleCnt); childObj.elapsed += child.elapsed; - if (!child.error) { + if (!(this.error === "0" || this.error === "")) { childObj.error = child.error; } }else{ @@ -60,6 +60,7 @@ export default class FlowElement { toTree(){ const ret = {}; ret["name"] = this.name; + ret["endTime"] = this.endTime? this.endTime : ""; ret["objName"] = this.objName ? this.objName : ""; ret["excludeObjName"] = this.excludeObjName; ret["threadName"] = this.threadName ? this.threadName : ""; @@ -67,8 +68,10 @@ export default class FlowElement { ret["type"] = this.type; ret["elapsed"] = isNaN(this.elapsed) ? 0 : this.elapsed; ret["txid"] = this.id; + ret["dupCount"] = this.dupleCnt; ret["children"] = []; ret["isError"] = this.error === "0" || this.error === "" ? false: true; + ret["tags"] = this.tags; for(const value of this.children.values()){ ret["children"].push(value.toTree()); diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.css b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.css index 27cc5ae..1264390 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.css +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.css @@ -46,6 +46,6 @@ } .xlog-flow .frame-xlog-flow-content > div > div { background-color: #fff; - height: calc(100% - 110px); + height: calc(100% - 60px); width: 100%; } diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js index b386c4f..c3dd45d 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js @@ -239,8 +239,7 @@ class XlogFlow extends Component { break; case Steps.THREAD_CALL_POSSIBLE: if(step.threaded === "0") break; - //- other thread call checking - + //- other thread call checking const yyyymmdd = moment(new Date(Number(thisElement.endTime))).format("YYYYMMDD"); const _url = `${getHttpProtocol(this.props.config)}/scouter/v1/xlog/${yyyymmdd}/${step.txid}`; jQuery.ajax({ @@ -253,8 +252,9 @@ class XlogFlow extends Component { setAuthHeader(xhr, this.props.config, getCurrentUser(this.props.config, this.props.user)); } }).done(data=>{ - step.elapsed = data.elapsed; + step.elapsed = data.result.elapsed; }); + const tcElement = this.FlowElement(ElementType.defaultProps.DISPATCH, step.txid + step.hash); tcElement.elapsed = Steps.toElapsedTime(step); tcElement.name = mainValue; @@ -542,14 +542,14 @@ class XlogFlow extends Component {
this.container = el }> - +
{ flowContent.show &&
- +
} diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.styl b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.styl index 4b55165..86820d5 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.styl +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.styl @@ -39,5 +39,5 @@ box-sizing: border-box & > div background-color: white - height: calc(100% - 110px) + height: calc(100% - 60px) width: 100% \ No newline at end of file diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowChart.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowChart.js index 8ea4399..0c84aa1 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowChart.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowChart.js @@ -1,14 +1,19 @@ import React from 'react' import connect from "react-redux/es/connect/connect"; import {withRouter} from "react-router-dom"; -function XlogFlowChart({ width, height, children }) { - return ( - - { - children - } - - ) + +class XlogFlowChart extends React.Component { + + render() { + const {width, height, children} = this.props; + return ( + + { + children + } + + ) + } } // XlogFlowChart.propTypes = { diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js index 0cf90d4..5ce2d4c 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js @@ -5,13 +5,14 @@ import * as d3 from "d3"; import d3tip from 'd3-tip'; import "./XlogFlowGraph.css"; import ElementType from "../../../../../../../common/ElementType"; - +import numeral from "numeral"; // # zoom in, zoom out // # resizing // # positing // # tooltip // # full info + class XlogFlowGraph extends Component { state = { @@ -111,7 +112,7 @@ class XlogFlowGraph extends Component { this.root.x0 = height / 2; this.root.y0 = 0; // this.min = elapsed.min ? elapsed.min.dup : 0; - this.max = elapsed.max ? elapsed.max.dup : 0; + this.max = elapsed.max.dup > 1000 ? elapsed.max.dup : 1000; // default max; this.xScale = d3 .scaleLinear() .range([0, 100]) @@ -149,6 +150,7 @@ class XlogFlowGraph extends Component { that.tip.hide(d,this); }) .on('click', (d)=> { + that.tip.hide(d,this); d3.event.stopPropagation(); this.props.clickContent(d.data); // that.scope.handleSelectSpan(d); @@ -181,7 +183,7 @@ class XlogFlowGraph extends Component { const {txid,isError} = d.data; const isOwner = txid === this.props.txid; if( isOwner ){ - return '#2529d8'; + return '#e03d60'; }else{ return !isError ? '#3d444f':'#E54C17'; } @@ -194,7 +196,12 @@ class XlogFlowGraph extends Component { .attr('fill', (d) => ElementType.defaultProps.toColor(d.data.type)) .attr("text-anchor",(d) => d.children || d._children ? "end" : "start") .style('font-size', '10px') - .text(d =>`${ElementType.defaultProps.toString(d.data.type)}`+ (d.data.type !== '0' ? `-${d.data.elapsed}ms` : '') ); + .text(d =>{ + const {type,elapsed,dupCount} = d.data; + const {numberFormat} = this.props.config; + const dupCnt = dupCount > 1 ? `(${dupCount})` : ''; + return `${ElementType.defaultProps.toString(type)}${dupCnt}`+ (type !== '0' ? `-${numeral(elapsed).format(numberFormat)}ms` : ''); + }); nodeEnter .append('rect') @@ -333,122 +340,4 @@ const mapStateToProps = (state) => { }; XlogFlowGraph = connect(mapStateToProps, undefined)(XlogFlowGraph); -export default withRouter(XlogFlowGraph); - -// -// update(source){ -// const treeData = this.treemap(source); -// -// // Compute the new tree layout. -// let nodes = treeData.descendants(),links = treeData.descendants().slice(1); -// -// // Normalize for fixed-depth. -// nodes.forEach(function(d){ d.y = d.depth * 180}); -// -// // ****************** Nodes section *************************** -// -// // Update the nodes... -// let i=0; -// let node = this.state.g.selectAll('g.node') -// .data(nodes, function(d) {return d.id || (d.id = ++i); }); -// -// // Enter any new modes at the parent's previous position. -// let nodeEnter = node.enter().append('g') -// .attr('class', 'node') -// .attr("transform", function(d) { -// return "translate(" + source.y0 + "," + source.x0 + ")"; -// }); -// // .on('click', click); -// -// // Add Circle for the nodes -// nodeEnter.append('circle') -// .attr('class', 'node') -// .attr('r', 1e-6) -// .style("fill", function(d) { -// return d._children ? "lightsteelblue" : "#fff"; -// }); -// -// // Add labels for the nodes -// nodeEnter.append('text') -// .attr("dy", ".35em") -// .attr("x", function(d) { -// return d.children || d._children ? -13 : 13; -// }) -// .attr("text-anchor", function(d) { -// return d.children || d._children ? "end" : "start"; -// }) -// .text(function(d) { return d.data.name; }); -// -// // UPDATE -// let nodeUpdate = nodeEnter.merge(node); -// -// // Transition to the proper position for the node -// nodeUpdate.transition() -// .duration(this.defaultProps.duration) -// .attr("transform", function(d) { -// return "translate(" + d.y + "," + d.x + ")"; -// }); -// -// // Update the node attributes and style -// nodeUpdate.select('circle.node') -// .attr('r', 10) -// .style("fill", function(d) { -// return d._children ? "lightsteelblue" : "#fff"; -// }) -// .attr('cursor', 'pointer'); -// -// -// // Remove any exiting nodes -// var nodeExit = node.exit().transition() -// .duration(this.defaultProps.duration) -// .attr("transform", function(d) { -// return "translate(" + source.y + "," + source.x + ")"; -// }) -// .remove(); -// -// // On exit reduce the node circles size to 0 -// nodeExit.select('circle') -// .attr('r', 1e-6); -// -// // On exit reduce the opacity of text labels -// nodeExit.select('text') -// .style('fill-opacity', 1e-6); -// -// // ****************** links section *************************** -// -// // Update the links... -// let link = this.state.g.selectAll('path.link') -// .data(links, function(d) { return d.id; }); -// -// // Enter any new links at the parent's previous position. -// let linkEnter = link.enter().insert('path', "g") -// .attr("class", "link") -// .attr('d', (d)=>{ -// const o = {x: source.x0, y: source.y0} -// return this.diagonal(o, o); -// }); -// -// // UPDATE -// let linkUpdate = linkEnter.merge(link); -// -// // Transition back to the parent element position -// linkUpdate.transition() -// .duration(this.defaultProps.duration) -// .attr('d', (d)=>this.diagonal(d, d.parent)); -// -// // Remove any exiting links -// let linkExit = link.exit().transition() -// .duration(this.defaultProps.duration) -// .attr('d', (d)=> { -// const o = {x: source.x, y: source.y}; -// return this.diagonal(o, o); -// }) -// .remove(); -// -// // Store the old positions for transition. -// nodes.forEach(function(d){ -// d.x0 = d.x; -// d.y0 = d.y; -// }); -// -// } \ No newline at end of file +export default withRouter(XlogFlowGraph); \ No newline at end of file diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.css b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.css index 25014b9..b7b831d 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.css +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.css @@ -24,3 +24,35 @@ height: calc(100% - 36px); overflow-y: auto; } +.xlog-flow-content .contents .flow-data { + margin-bottom: 20px; + padding: 5px 10px; +} +.xlog-flow-content .contents .flow-data > div { + white-space: nowrap; + display: table; + table-layout: fixed; + width: 100%; +} +.xlog-flow-content .contents .flow-data span { + padding: 7px 10px; + box-sizing: border-box !important; + display: table-cell; +} +.xlog-flow-content .contents .flow-data .label { + width: 100px; + background-color: #aeaeae; +} +.xlog-flow-content .contents .flow-data .data { + white-space: nowrap; +} +.xlog-flow-content .contents .xlog-flow-btn { + position: relative; + margin: 0px auto 5px; + border-radius: 5px; + font-size: 11px; + cursor: pointer; + background: #ff0; + padding: 6px 8px; + display: inline-block !important; +} diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js index fd49821..bb1cb5a 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js @@ -3,12 +3,121 @@ import {withRouter} from "react-router-dom"; import {connect} from "react-redux"; import "./XlogFlowContent.css" import {IdAbbr} from "../../../../../../../common/idAbbr"; +// import moment from "moment"; +import ElementType from "../../../../../../../common/ElementType"; +import * as d3 from "d3"; +import numeral from "numeral"; + +const contents = [ + { + key : 'txid', + show : true, + dip : 'Transaction ID', + type : 'scouter-tx', + unit : '' + }, + { + key : 'endTime', + dip : 'EndTime', + type : 'date', + unit : '', + show : true, + }, + { + key : 'elapsed', + show : true, + dip : 'Duration', + type : 'number', + unit : 'ms' + }, + { + key : 'excludeObjName', + dip : '', + type : 'boolean', + unit : '', + show : false, + }, + { + key : 'type', + show : true, + dip : 'Profile Type', + type : 'profile-category', + unit : '' + }, + { + key : 'threadName', + show : true, + dip : 'Thread Name', + type : 'string', + unit : '' + }, + { + key : 'address', + show : true, + dip : 'Address', + type : 'string', + unit : '' + }, + { + key : 'objName', + show : true, + dip : 'Instance Name', + type : 'string', + unit : '' + }, + { + key : 'name', + dip : 'EndPoint', + type : 'string', + unit : '', + show : false, + } +]; + class XlogFlowContent extends Component { + close=()=>{ this.props.close(); }; + click=()=>{ + const {txid,endTime} = this.props.content; + this.props.txBtnClick({txid: txid,endTime:endTime}); + }; + isTxFlow(){ + const {type} = this.props.content; + switch (type) { + case ElementType.defaultProps.SERVICE: + case ElementType.defaultProps.DISPATCH: + case ElementType.defaultProps.THREAD: + return true; + default: + return false; + } + } + dataTodisplay(meta,value){ + const {numberFormat,timeFormat,dateFormat} = this.props.config; + const fullTimeFormat = `${dateFormat} ${timeFormat}`; + let cov; + switch(meta.type){ + case 'date': + cov = d3.timeFormat(fullTimeFormat)(new Date(Number(value))); + break + case 'number': + cov= numeral(value).format(numberFormat); + break + case 'scouter-tx': + cov = `${IdAbbr.abbr(value)} (${value})`; + break; + case 'profile-category': + cov = ElementType.defaultProps.toString(value); + break; + default: + cov=value; + } + return `${cov} ${meta.unit}`; + } render(){ return (
@@ -17,9 +126,32 @@ class XlogFlowContent extends Component {
-
-                        {this.props.content.name}
-                    
+
+ + {contents.filter(d => d.show) + .filter(d => this.props.content[d.key] ? true : false) + .map(d => +
+ {d.dip} + {this.dataTodisplay(d,this.props.content[d.key])} +
+ ) + } + { + this.isTxFlow() ? +
+ EndPoint + {this.props.content.name} +
+ : +
+
{this.props.content.name}
+
+ } +
+ {this.isTxFlow() &&
+
TRY TX FLOW
+
}
) diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.styl b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.styl index edbba1d..0d5fe56 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.styl +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.styl @@ -19,3 +19,41 @@ width: 100% height: calc(100% - 36px) overflow-y : auto + .flow-data + margin-bottom: 20px + padding : 5px 10px + & > div + white-space: nowrap + display: table + table-layout: fixed + width: 100% + // & div.span + // padding: 7px 10px + // box-sizing: border-box + // display: table-cell + // .label + // width: 100px + // background-color: #608cee + // .data + // white-space: nowrap; + & span + padding: 7px 10px + box-sizing: border-box !important + display: table-cell + .label + width: 100px + background-color: #aeaeae + .data + white-space: nowrap; + .xlog-flow-btn + position: relative + margin:0px auto 5px + border-radius : 5px + font-size : 11px + cursor : pointer + background : yellow + padding : 6px 8px + display: inline-block !important + + + From b1d1e7e22015c86eb6315a1cc0671be4458d32f4 Mon Sep 17 00:00:00 2001 From: kranian Date: Fri, 18 Oct 2019 16:54:51 +0900 Subject: [PATCH 40/77] tx elapsed time calc max fixed #218 --- .../FrameProfile/XlogFlow/FlowElement.js | 23 ++++--------------- .../XlogFlow/XlogFlowChart/XlogFlowGraph.js | 14 +++++++++-- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js index aa32ac5..8af34b8 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js @@ -1,5 +1,5 @@ import ElementType from "../../../../../../common/ElementType.js"; -import * as _ from 'lodash'; + export default class FlowElement { name = null; @@ -39,7 +39,7 @@ export default class FlowElement { } } toArray(){ - const ret = [{id : this.id,elapsed : this.elapsed} ]; + const ret = [{id : this.id, elapsed : this.elapsed} ]; for(const value of this.children.values()){ ret.push(value); } @@ -79,23 +79,10 @@ export default class FlowElement { return ret; } toElaped(){ - const elaps = []; + const elaps = [{id : this.id, dup : this.elapsed} ]; for(const value of this.children.values()){ - elaps.push( - value.toArray() - .filter(_d => _d.elapsed > 0 ) - .map(_d => { - return { - id :_d.id, - dup : _d.elapsed - }}) - ); - - } - const flatMap = _.flatMapDeep(elaps); - return { - min : _.minBy(flatMap,(_d) => _d.dup), - max : _.maxBy(flatMap,(_d) => _d.dup) + elaps.push(value.toElaped()); } + return elaps; } } \ No newline at end of file diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js index 5ce2d4c..443b634 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js @@ -3,6 +3,7 @@ import {withRouter} from "react-router-dom"; import React,{Component} from "react"; import * as d3 from "d3"; import d3tip from 'd3-tip'; +import * as _ from 'lodash' import "./XlogFlowGraph.css"; import ElementType from "../../../../../../../common/ElementType"; import numeral from "numeral"; @@ -51,8 +52,16 @@ class XlogFlowGraph extends Component { getTreeData(data){ const [[,value]]= [...data]; + + const max = _(value.toElaped()) + .flatMapDeep() + .map(d=>d.dup) + .max(); return { - elapsed : value.toElaped(), + elapsed : { + max : max, + min : 0 + }, tree : value.toTree() } } @@ -112,7 +121,8 @@ class XlogFlowGraph extends Component { this.root.x0 = height / 2; this.root.y0 = 0; // this.min = elapsed.min ? elapsed.min.dup : 0; - this.max = elapsed.max.dup > 1000 ? elapsed.max.dup : 1000; // default max; + this.max = elapsed.max ? elapsed.max : 0; // default max; + this.xScale = d3 .scaleLinear() .range([0, 100]) From e0333cefd323af9aef16c28785abe49443581e8b Mon Sep 17 00:00:00 2001 From: kranian Date: Fri, 18 Oct 2019 17:05:49 +0900 Subject: [PATCH 41/77] exp error calc fixed --- .../Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js index 8af34b8..79735a0 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js @@ -30,9 +30,7 @@ export default class FlowElement { if(childObj){ childObj.dupleCnt += Number(child.dupleCnt); childObj.elapsed += child.elapsed; - if (!(this.error === "0" || this.error === "")) { - childObj.error = child.error; - } + childObj.error = child.error; }else{ child.parent = this.id; this.children.set(child.id,child); From 2f3e830e56eb497cb4a5331751d8f6719eb7fc81 Mon Sep 17 00:00:00 2001 From: "yeosong.heo" Date: Sat, 19 Oct 2019 10:54:17 +0900 Subject: [PATCH 42/77] :construction: flow content design --- .../XlogFlowContent/XlogFlowContent.css | 25 ++++--- .../XlogFlowContent/XlogFlowContent.js | 70 ++++++++++--------- .../XlogFlowContent/XlogFlowContent.styl | 48 +++++++------ 3 files changed, 81 insertions(+), 62 deletions(-) diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.css b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.css index b7b831d..cc61822 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.css +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.css @@ -20,13 +20,18 @@ } .xlog-flow-content .contents { position: relative; - width: 100%; + width: calc(100% - 30px); height: calc(100% - 36px); overflow-y: auto; + margin: 10px 10px; +} +.xlog-flow-content .contents .sub-title { + background-color: #333; + color: #fff; + border: 1px solid #333; } .xlog-flow-content .contents .flow-data { margin-bottom: 20px; - padding: 5px 10px; } .xlog-flow-content .contents .flow-data > div { white-space: nowrap; @@ -41,18 +46,18 @@ } .xlog-flow-content .contents .flow-data .label { width: 100px; - background-color: #aeaeae; + background-color: #eee; } .xlog-flow-content .contents .flow-data .data { white-space: nowrap; } -.xlog-flow-content .contents .xlog-flow-btn { - position: relative; - margin: 0px auto 5px; - border-radius: 5px; - font-size: 11px; +.xlog-flow-content .contents .flow-data .txid { cursor: pointer; - background: #ff0; - padding: 6px 8px; + color: #7b3e81; + text-decoration: underline; +} +.xlog-flow-content .contents .type { + position: relative; + margin-top: 10px; display: inline-block !important; } diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js index bb1cb5a..508dd93 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js @@ -3,6 +3,7 @@ import {withRouter} from "react-router-dom"; import {connect} from "react-redux"; import "./XlogFlowContent.css" import {IdAbbr} from "../../../../../../../common/idAbbr"; +import sqlFormatter from "sql-formatter"; // import moment from "moment"; import ElementType from "../../../../../../../common/ElementType"; import * as d3 from "d3"; @@ -12,7 +13,7 @@ const contents = [ { key : 'txid', show : true, - dip : 'Transaction ID', + dip : 'Txid', type : 'scouter-tx', unit : '' }, @@ -39,7 +40,7 @@ const contents = [ }, { key : 'type', - show : true, + show : false, dip : 'Profile Type', type : 'profile-category', unit : '' @@ -80,11 +81,17 @@ class XlogFlowContent extends Component { close=()=>{ this.props.close(); }; - click=()=>{ - const {txid,endTime} = this.props.content; - this.props.txBtnClick({txid: txid,endTime:endTime}); - }; + click=(target)=>{ + if(target === 'txid') { + const {txid, endTime} = this.props.content; + this.props.txBtnClick({txid: txid, endTime: endTime}); + } + }; + getProfileType(){ + const {type} = this.props.content; + return ElementType.defaultProps.toString(type) + } isTxFlow(){ const {type} = this.props.content; switch (type) { @@ -108,11 +115,14 @@ class XlogFlowContent extends Component { cov= numeral(value).format(numberFormat); break case 'scouter-tx': - cov = `${IdAbbr.abbr(value)} (${value})`; + cov = `${IdAbbr.abbr(value)}`; break; case 'profile-category': cov = ElementType.defaultProps.toString(value); break; + case 'SQL': + cov = sqlFormatter.format(value,{indent : " "}); + break; default: cov=value; } @@ -122,36 +132,32 @@ class XlogFlowContent extends Component { return (
- FLOW CONTENTS - {IdAbbr.abbr(this.props.content.txid)} + FLOW CONTENTS - {IdAbbr.abbr(this.props.content.txid)}(TXID : {this.props.content.txid})
+
+ FLOW INFO +
- - {contents.filter(d => d.show) - .filter(d => this.props.content[d.key] ? true : false) - .map(d => -
- {d.dip} - {this.dataTodisplay(d,this.props.content[d.key])} -
- ) - } - { - this.isTxFlow() ? -
- EndPoint - {this.props.content.name} -
- : -
-
{this.props.content.name}
-
- } + {contents.filter(d => d.show) + .filter(d => this.props.content[d.key] ? true : false) + .map(d => +
+ {d.dip} + this.click(d.key)}>{this.dataTodisplay(d,this.props.content[d.key])} +
+ ) + } +
+
+ FLOW TYPE - {this.getProfileType()}
- {this.isTxFlow() &&
-
TRY TX FLOW
-
} +
+ {this.dataTodisplay({type : this.getProfileType(), unit : ''},this.props.content.name)} +
+ +
) diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.styl b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.styl index 0d5fe56..c811652 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.styl +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.styl @@ -9,6 +9,7 @@ & span display: inline-block padding: 4px + .close-btn position: absolute top : 7px @@ -16,44 +17,51 @@ display: inline-block !important .contents position: relative - width: 100% + width: calc(100% - 30px) height: calc(100% - 36px) overflow-y : auto + margin: 10px 10px + .sub-title + //padding: 10px !important; + background-color: #333 + color: white + //display: + border: 1px solid #333 .flow-data margin-bottom: 20px - padding : 5px 10px & > div white-space: nowrap display: table table-layout: fixed width: 100% - // & div.span - // padding: 7px 10px - // box-sizing: border-box - // display: table-cell - // .label - // width: 100px - // background-color: #608cee - // .data - // white-space: nowrap; & span padding: 7px 10px box-sizing: border-box !important display: table-cell .label width: 100px - background-color: #aeaeae + background-color: #EEE .data - white-space: nowrap; - .xlog-flow-btn + white-space: nowrap + .txid + cursor:pointer + color:#7B3E81 + text-decoration:underline + .type position: relative - margin:0px auto 5px - border-radius : 5px - font-size : 11px - cursor : pointer - background : yellow - padding : 6px 8px + margin-top : 10px display: inline-block !important + // + // + //.xlog-flow-btn + // position: relative + // margin:0px auto 5px + // border-radius : 5px + // font-size : 11px + // cursor : pointer + // background : yellow + // padding : 6px 8px + // display: inline-block !important From 5c8f950e9d95ca1326b512e7cfe0e04eb4438ab6 Mon Sep 17 00:00:00 2001 From: "yeosong.heo" Date: Sat, 19 Oct 2019 12:23:18 +0900 Subject: [PATCH 43/77] :bug: draw source target fixed --- .../FrameProfile/XlogFlow/XlogFlow.js | 2 +- .../XlogFlow/XlogFlowChart/XlogFlowGraph.js | 42 ++++++++++--------- .../XlogFlowContent/XlogFlowContent.js | 5 ++- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js index c3dd45d..d2fbb5b 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js @@ -549,7 +549,7 @@ class XlogFlow extends Component { flowContent.show &&
- +
} diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js index 443b634..e9f346d 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js @@ -19,15 +19,19 @@ class XlogFlowGraph extends Component { state = { g: null, }; + constructor(props){ + super(props); + this.properies ={ + height: 500, + width: 400, + duration : 500 ,//ms, + margin: { + top: 15, right: 15, bottom: 25, left: 40 + }, + }; + } + - defaultProps ={ - height: 500, - width: 400, - duration : 500 ,//ms, - margin: { - top: 15, right: 15, bottom: 25, left: 40 - }, - }; // // // constructor(props){ @@ -36,11 +40,11 @@ class XlogFlowGraph extends Component { componentWillReceiveProps(nextProps){ if (nextProps.resize !== this.props.resize) { - this.defaultProps.height = nextProps.resize.height; - this.defaultProps.width = nextProps.resize.width; - this.init(this.getTreeData(nextProps.xlogflow)) + this.properies.height = nextProps.resize.height; + this.properies.width = nextProps.resize.width; + this.init(this.getTreeData(nextProps.xlogflow)); }else if (nextProps.xlogflow !== this.props.xlogflow) { - this.draw(this.getTreeData(nextProps.xlogflow)); + this.init(this.getTreeData(nextProps.xlogflow)); } } @@ -114,7 +118,7 @@ class XlogFlowGraph extends Component { }; init(data){ - const {height,width} = this.defaultProps; + const {height,width} = this.properies; const {elapsed,tree} = data; this.treemap = d3.tree().size([height, width]); this.root = d3.hierarchy(tree, d => d.children); @@ -132,12 +136,12 @@ class XlogFlowGraph extends Component { this.topSlow=[]; this.topChild=[]; this.root.children.forEach((d)=>this.collapse(d)); - this.draw(tree); + this.draw(this.root); } draw(source) { const that = this; // Assigns the x and y position for the nodes - let treeData = this.treemap(this.root); + let treeData = this.treemap(source); // Compute the new tree layout. let nodes = treeData.descendants(), links = treeData.descendants().slice(1); @@ -239,7 +243,7 @@ class XlogFlowGraph extends Component { let nodeUpdate = nodeEnter.merge(node); nodeUpdate.transition() - .duration(this.defaultProps.duration) + .duration(this.properies.duration) .attr('transform', function(d) { return 'translate(' + d.y + ',' + d.x + ')'; }); @@ -258,7 +262,7 @@ class XlogFlowGraph extends Component { // Remove any exiting nodes let nodeExit = node.exit().transition() - .duration(this.defaultProps.duration) + .duration(this.properies.duration) .attr('transform', function(d) { return 'translate(' + source.y + ',' + source.x + ')'; }) @@ -288,11 +292,11 @@ class XlogFlowGraph extends Component { let linkUpdate = linkEnter.merge(link); linkUpdate.transition() - .duration(this.defaultProps.duration) + .duration(this.properies.duration) .attr('d', function(d){ return diagonal(d, d.parent) }); //linkExit link.exit().transition() - .duration(this.defaultProps.duration) + .duration(this.properies.duration) .attr('d', function(d) { var o = {x: source.x, y: source.y} return diagonal(o, o) diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js index 508dd93..18e644d 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js @@ -93,12 +93,13 @@ class XlogFlowContent extends Component { return ElementType.defaultProps.toString(type) } isTxFlow(){ - const {type} = this.props.content; + + const {type,txid} = this.props.content; switch (type) { case ElementType.defaultProps.SERVICE: case ElementType.defaultProps.DISPATCH: case ElementType.defaultProps.THREAD: - return true; + return this.props.thisTxid !== txid; default: return false; } From b40295d82cc52ccf301c79950f0993ff5110d179 Mon Sep 17 00:00:00 2001 From: "yeosong.heo" Date: Sun, 20 Oct 2019 01:33:16 +0900 Subject: [PATCH 44/77] :bug: sql dictionary adding --- http/xflow.http | 5 ++- .../FrameProfile/XlogFlow/FlowElement.js | 7 +++- .../FrameProfile/XlogFlow/XlogFlow.js | 30 +++++++++++++- .../XlogFlowContent/XlogFlowContent.css | 18 ++++++--- .../XlogFlowContent/XlogFlowContent.js | 39 +++++++++++++++++-- .../XlogFlowContent/XlogFlowContent.styl | 16 +++++--- 6 files changed, 98 insertions(+), 17 deletions(-) diff --git a/http/xflow.http b/http/xflow.http index 934c4f0..4e66bdc 100644 --- a/http/xflow.http +++ b/http/xflow.http @@ -68,4 +68,7 @@ GET http://demo.scouterapm.com:6188/scouter/v1/xlog/20191017/374225541302635030 GET http://demo.scouterapm.com:6188/scouter/v1/xlog-data/20191017/-854231684852475423 -### \ No newline at end of file +### +GET http://demo.scouterapm.com:6188/scouter/v1/dictionary/20191020?dictKeys=[table:566700965] + +### diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js index 79735a0..fd1659b 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/FlowElement.js @@ -68,7 +68,8 @@ export default class FlowElement { ret["txid"] = this.id; ret["dupCount"] = this.dupleCnt; ret["children"] = []; - ret["isError"] = this.error === "0" || this.error === "" ? false: true; + ret["isError"] = this.isError(); + ret["error"] = this.error; ret["tags"] = this.tags; for(const value of this.children.values()){ @@ -76,6 +77,10 @@ export default class FlowElement { } return ret; } + isError(){ + return this.error === "0" || this.error === "" ? false: true; + } + toElaped(){ const elaps = [{id : this.id, dup : this.elapsed} ]; for(const value of this.children.values()){ diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js index d2fbb5b..5aa0ff5 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js @@ -190,6 +190,7 @@ class XlogFlow extends Component { apiElement.error = step.error; apiElement.name = mainValue; apiElement.address = step.address; + apiElement.endTime = thisElement.endTime; if(step.txid !== "0"){ //other call check const callElement = serviceMap.get(step.txid); @@ -209,6 +210,7 @@ class XlogFlow extends Component { spanCallElement.error = step.error; spanCallElement.name = mainValue; spanCallElement.address = step.address; + spanCallElement.endTime = thisElement.endTime; if (step.txid !== "0") { const callElement = serviceMap.get(step.txid); if (callElement) { @@ -226,6 +228,7 @@ class XlogFlow extends Component { dispatchElement.elapsed = Steps.toElapsedTime(step); dispatchElement.error = step.error; dispatchElement.name = mainValue; + dispatchElement.endTime = thisElement.endTime; if (step.txid !== "0") { const callElement = serviceMap.get(step.txid); if (callElement) { @@ -258,6 +261,8 @@ class XlogFlow extends Component { const tcElement = this.FlowElement(ElementType.defaultProps.DISPATCH, step.txid + step.hash); tcElement.elapsed = Steps.toElapsedTime(step); tcElement.name = mainValue; + tcElement.endTime = thisElement.endTime; + if (step.txid !== "0") { const callElement = serviceMap.get(step.txid); if (callElement) { @@ -275,16 +280,38 @@ class XlogFlow extends Component { apiSumElement.elapsed = Steps.toElapsedTime(step); apiSumElement.error = step.error; apiSumElement.name = mainValue; + apiSumElement.endTime = thisElement.endTime; thisElement.addChild(apiSumElement); break; case Steps.SQL: case Steps.SQL2: case Steps.SQL3: const sqlElement = this.FlowElement(ElementType.defaultProps.SQL, step.hash); + let relMainValue = mainValue; + jQuery.ajax({ + method: "GET", + async: false, + dataType: "json", + url: `${getHttpProtocol(this.props.config)}/scouter/v1/dictionary/${moment(new Date(Number(thisElement.endTime))).format("YYYYMMDD")}?dictKeys=[table:${step.hash}]`, + xhrFields: getWithCredentials(this.props.config), + beforeSend: (xhr)=>{ + setAuthHeader(xhr, this.props.config, getCurrentUser(this.props.config, this.props.user)); + } + }).done(data=>{ + const res = data.result[0]; + if(res) { + relMainValue = res.text; + } + }); + sqlElement.elapsed = Steps.toElapsedTime(step); - sqlElement.name = mainValue; + sqlElement.name = relMainValue; sqlElement.error = step.error; sqlElement.tags.sql = mainValue; + sqlElement.tags.param = step.param; + sqlElement.tags.prefix = step.xtypePrefix; + sqlElement.endTime = thisElement.endTime; + thisElement.addChild(sqlElement); break; case Steps.SQL_SUM: @@ -294,6 +321,7 @@ class XlogFlow extends Component { sqlSumElement.error = step.error; sqlSumElement.name = mainValue; sqlSumElement.tags.sql=mainValue; + sqlSumElement.endTime = thisElement.endTime; thisElement.addChild(sqlSumElement); break; case Steps.THREAD_SUBMIT: diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.css b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.css index cc61822..54375ee 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.css +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.css @@ -3,7 +3,7 @@ } .xlog-flow-content .title { padding: 8px; - background-color: #333; + background-color: #666; color: #fff; height: 36px; box-sizing: border-box; @@ -20,10 +20,14 @@ } .xlog-flow-content .contents { position: relative; - width: calc(100% - 30px); - height: calc(100% - 36px); - overflow-y: auto; + width: calc(100% - 12px); + height: calc(100% - 50px); margin: 10px 10px; + overflow-y: auto; +} +.xlog-flow-content .contents::-webkit-scrollbar { + width: 7px; + height: 7px; } .xlog-flow-content .contents .sub-title { background-color: #333; @@ -59,5 +63,9 @@ .xlog-flow-content .contents .type { position: relative; margin-top: 10px; - display: inline-block !important; + display: block !important; +} +.xlog-flow-content .contents .error { + color: #f00; + display: block !important; } diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js index 18e644d..19d5290 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js @@ -8,6 +8,9 @@ import sqlFormatter from "sql-formatter"; import ElementType from "../../../../../../../common/ElementType"; import * as d3 from "d3"; import numeral from "numeral"; +import moment from "moment/moment"; +import {getCurrentUser, getHttpProtocol, getWithCredentials, setAuthHeader} from "../../../../../../../common/common"; +import jQuery from "jquery"; const contents = [ { @@ -104,6 +107,28 @@ class XlogFlowContent extends Component { return false; } } + getError(){ + let ret = ''; + const {endTime,error} = this.props.content; + + jQuery.ajax({ + method: "GET", + async: false, + dataType: "json", + url: `${getHttpProtocol(this.props.config)}/scouter/v1/dictionary/${moment(new Date(Number(endTime))).format("YYYYMMDD")}?dictKeys=[error:${error}]`, + xhrFields: getWithCredentials(this.props.config), + beforeSend: (xhr)=>{ + setAuthHeader(xhr, this.props.config, getCurrentUser(this.props.config, this.props.user)); + } + }).done(data=>{ + const res = data.result[0]; + if(res) { + ret = res.text + } + }); + return ret; + } + dataTodisplay(meta,value){ const {numberFormat,timeFormat,dateFormat} = this.props.config; const fullTimeFormat = `${dateFormat} ${timeFormat}`; @@ -130,13 +155,14 @@ class XlogFlowContent extends Component { return `${cov} ${meta.unit}`; } render(){ + const isSQL = this.getProfileType() === 'SQL'; return (
FLOW CONTENTS - {IdAbbr.abbr(this.props.content.txid)}(TXID : {this.props.content.txid})
-
+
FLOW INFO
@@ -154,10 +180,14 @@ class XlogFlowContent extends Component {
FLOW TYPE - {this.getProfileType()}
-
- {this.dataTodisplay({type : this.getProfileType(), unit : ''},this.props.content.name)} +
+ {this.dataTodisplay({type : this.getProfileType(), unit : ''}, isSQL ? this.props.content.tags.sql : this.props.content.name)}
- + { + this.props.content.isError &&
+ {this.getError()} +
+ }
@@ -167,6 +197,7 @@ class XlogFlowContent extends Component { const mapStateToProps = (state) => { return { config: state.config, + user : state.user }; }; XlogFlowContent = connect(mapStateToProps, null)(XlogFlowContent); diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.styl b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.styl index c811652..a8f4a77 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.styl +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.styl @@ -2,7 +2,7 @@ position: relative .title padding: 8px - background-color: #333 + background-color: #666 color: white height: 36px box-sizing: border-box @@ -17,10 +17,13 @@ display: inline-block !important .contents position: relative - width: calc(100% - 30px) - height: calc(100% - 36px) - overflow-y : auto + width: calc(100% - 12px) + height: calc(100% - 50px) margin: 10px 10px + overflow-y : auto + &::-webkit-scrollbar + width: 7px + height: 7px .sub-title //padding: 10px !important; background-color: #333 @@ -50,7 +53,10 @@ .type position: relative margin-top : 10px - display: inline-block !important + display: block !important + .error + color: red + display : block !important // // //.xlog-flow-btn From 949ba15034895bd328c9383f1c6b34ef86be43cf Mon Sep 17 00:00:00 2001 From: "yeosong.heo" Date: Sun, 20 Oct 2019 01:35:38 +0900 Subject: [PATCH 45/77] :construction: flow content design title color change --- .../FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.css | 2 +- .../FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.styl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.css b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.css index 54375ee..7a7026d 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.css +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.css @@ -3,7 +3,7 @@ } .xlog-flow-content .title { padding: 8px; - background-color: #666; + background-color: #444; color: #fff; height: 36px; box-sizing: border-box; diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.styl b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.styl index a8f4a77..c1ba835 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.styl +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.styl @@ -2,7 +2,7 @@ position: relative .title padding: 8px - background-color: #666 + background-color: #444 color: white height: 36px box-sizing: border-box From 90359c6ba31a61a49f1ba5af64b5e40a353c45f4 Mon Sep 17 00:00:00 2001 From: "yeosong.heo" Date: Mon, 21 Oct 2019 00:43:55 +0900 Subject: [PATCH 46/77] :zap: flow sqltext viewer adding, flow graph click event action fixed --- .../FrameProfile/XlogFlow/XlogFlow.js | 2 +- .../XlogFlow/XlogFlowChart/XlogFlowGraph.js | 6 +++-- .../XlogFlowContent/SQLText/SQLText.css | 22 ++++++++++++++++ .../XlogFlowContent/SQLText/SQLText.js | 25 +++++++++++++++++++ .../XlogFlowContent/SQLText/SQLText.styl | 17 +++++++++++++ .../XlogFlowContent/XlogFlowContent.css | 12 +++++++++ .../XlogFlowContent/XlogFlowContent.js | 15 +++++------ .../XlogFlowContent/XlogFlowContent.styl | 10 ++++++++ 8 files changed, 99 insertions(+), 10 deletions(-) create mode 100644 src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/SQLText/SQLText.css create mode 100644 src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/SQLText/SQLText.js create mode 100644 src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/SQLText/SQLText.styl diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js index 5aa0ff5..3ea6611 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js @@ -299,7 +299,7 @@ class XlogFlow extends Component { } }).done(data=>{ const res = data.result[0]; - if(res) { + if(res && res.text) { relMainValue = res.text; } }); diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js index e9f346d..2a286fa 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowChart/XlogFlowGraph.js @@ -141,7 +141,7 @@ class XlogFlowGraph extends Component { draw(source) { const that = this; // Assigns the x and y position for the nodes - let treeData = this.treemap(source); + let treeData = this.treemap(this.root); // Compute the new tree layout. let nodes = treeData.descendants(), links = treeData.descendants().slice(1); @@ -329,11 +329,13 @@ class XlogFlowGraph extends Component { let dur = d.elapsed; // d.children.forEach((_d) => dur -= _d.elapsed); // + d._children = d.children; d.dur = d.elapsed; this.topSlow.push(dur); this.topChild.push(d.children.length); d.childrenLength = d.children.length; - d.children.forEach(_d=>this.collapse(_d)) + d.children.forEach(_d=>this.collapse(_d)); + // d.children = null } }; diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/SQLText/SQLText.css b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/SQLText/SQLText.css new file mode 100644 index 0000000..17fd6de --- /dev/null +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/SQLText/SQLText.css @@ -0,0 +1,22 @@ +.sql-text { + width: calc(100% - 12px); + height: calc(100% - 50px); + margin-top: 10px; +} +.sql-text::-webkit-scrollbar { + width: 7px; + height: 7px; +} +.sql-text .prefix { + color: #006400 !important; +} +.sql-text .sql-statement { + line-height: 140%; + color: #0000c3; + white-space: nowrap; + overflow-x: auto; + width: 100%; +} +.sql-text .formatter { + white-space: pre; +} diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/SQLText/SQLText.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/SQLText/SQLText.js new file mode 100644 index 0000000..74c5ee7 --- /dev/null +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/SQLText/SQLText.js @@ -0,0 +1,25 @@ +import React from "react"; +import sqlFormatter from "sql-formatter"; +import "./SQLText.css" + +export default class SQLText extends React.Component{ + + + render(){ + const {sql,prefix} = this.props.meta; + return
+
+ {prefix} + { + sqlFormatter.format(sql,{ + indent: " " + }) + } +
+
+ + + } + + +} diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/SQLText/SQLText.styl b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/SQLText/SQLText.styl new file mode 100644 index 0000000..de68b40 --- /dev/null +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/SQLText/SQLText.styl @@ -0,0 +1,17 @@ +.sql-text + width: calc(100% - 12px) + height: calc(100% - 50px) + margin-top : 10px + &::-webkit-scrollbar + width: 7px + height: 7px + .prefix + color: darkgreen !important + .sql-statement + line-height: 140% + color: #0000C3 + white-space: nowrap + overflow-x: auto + width: 100% + .formatter + white-space : pre diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.css b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.css index 7a7026d..8c24879 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.css +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.css @@ -65,6 +65,18 @@ margin-top: 10px; display: block !important; } +.xlog-flow-content .contents .type > span { + display: inline-block; + padding: 4px; + background-color: #333; + color: #fff; + background-color: #eee; + color: #333; + border-radius: 2px; +} +.xlog-flow-content .contents .type .info { + margin-top: 10px; +} .xlog-flow-content .contents .error { color: #f00; display: block !important; diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js index 19d5290..d15b8f9 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js @@ -1,9 +1,9 @@ import React,{Component} from "react"; import {withRouter} from "react-router-dom"; import {connect} from "react-redux"; +import SQLText from "./SQLText/SQLText" import "./XlogFlowContent.css" import {IdAbbr} from "../../../../../../../common/idAbbr"; -import sqlFormatter from "sql-formatter"; // import moment from "moment"; import ElementType from "../../../../../../../common/ElementType"; import * as d3 from "d3"; @@ -146,9 +146,6 @@ class XlogFlowContent extends Component { case 'profile-category': cov = ElementType.defaultProps.toString(value); break; - case 'SQL': - cov = sqlFormatter.format(value,{indent : " "}); - break; default: cov=value; } @@ -178,14 +175,18 @@ class XlogFlowContent extends Component { }
- FLOW TYPE - {this.getProfileType()} + FLOW TYPE
-
- {this.dataTodisplay({type : this.getProfileType(), unit : ''}, isSQL ? this.props.content.tags.sql : this.props.content.name)} +
+ {this.getProfileType()} + {isSQL ? :
{this.props.content.name}
}
{ this.props.content.isError &&
+ ERROR +
{this.getError()} +
} diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.styl b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.styl index c1ba835..c4c3bdf 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.styl +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.styl @@ -54,6 +54,16 @@ position: relative margin-top : 10px display: block !important + & > span + display: inline-block + padding: 4px + background-color: #333 + color: white + background-color: #EEE + color: #333 + border-radius: 2px + .info + margin-top : 10px .error color: red display : block !important From 068b3dc2b54031cfb854c2dce6b85a96b25530b1 Mon Sep 17 00:00:00 2001 From: "yeosong.heo" Date: Mon, 21 Oct 2019 01:04:39 +0900 Subject: [PATCH 47/77] :bug: fixed error hash code request --- .../FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js index d15b8f9..6ce80bd 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js @@ -110,7 +110,10 @@ class XlogFlowContent extends Component { getError(){ let ret = ''; const {endTime,error} = this.props.content; - + const re = /^[0-9.]+$/; + if(!(error.match(re))){ + return error; + } jQuery.ajax({ method: "GET", async: false, From b4cfbb0a974f212a05a7748366111fa8ba1bf07f Mon Sep 17 00:00:00 2001 From: kranian Date: Mon, 21 Oct 2019 13:18:27 +0900 Subject: [PATCH 48/77] :bug : exp error reg check --- .../FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js index 6ce80bd..c307f07 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js @@ -110,10 +110,11 @@ class XlogFlowContent extends Component { getError(){ let ret = ''; const {endTime,error} = this.props.content; - const re = /^[0-9.]+$/; - if(!(error.match(re))){ + + if(isNaN(Number(error))){ return error; } + jQuery.ajax({ method: "GET", async: false, From 3589ccf9714aa9ddbd731bc7281d95252c4aa550 Mon Sep 17 00:00:00 2001 From: kranian Date: Mon, 21 Oct 2019 15:05:27 +0900 Subject: [PATCH 49/77] :zap: xlog-flow chart gray theme --- src/Theme.css | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Theme.css b/src/Theme.css index a3150e4..0ccbd08 100644 --- a/src/Theme.css +++ b/src/Theme.css @@ -350,3 +350,16 @@ html.theme-gray, .theme-gray body { background-color: #111; } +.theme-gray div.xlog-flow > div.contents { + background-color: #666; +} +.theme-gray div.xlog-flow-content > div.contents > div.flow-data span{ + color : #333; +} +.theme-gray div.xlog-flow-content > div.contents > div.flow-data span.data.txid { + color : #7B3E81; +} +.theme-gray div.xlog-flow-content > div.contents > div.type > div.info{ + color : #333; +} + From 5f0a3d504152774fabc162807486825ec4233622 Mon Sep 17 00:00:00 2001 From: kranian Date: Mon, 21 Oct 2019 16:13:12 +0900 Subject: [PATCH 50/77] :bug: none service xlog click event --- .../XlogFlow/XlogFlowContent/XlogFlowContent.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js index c307f07..94723c7 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js @@ -88,7 +88,10 @@ class XlogFlowContent extends Component { click=(target)=>{ if(target === 'txid') { const {txid, endTime} = this.props.content; - this.props.txBtnClick({txid: txid, endTime: endTime}); + const {thisTxid} = this.props; + if(thisTxid !== txid) { + this.props.txBtnClick({txid: txid, endTime: endTime}); + } } }; getProfileType(){ @@ -98,11 +101,13 @@ class XlogFlowContent extends Component { isTxFlow(){ const {type,txid} = this.props.content; + const {thisTxid} = this.props; + switch (type) { case ElementType.defaultProps.SERVICE: case ElementType.defaultProps.DISPATCH: case ElementType.defaultProps.THREAD: - return this.props.thisTxid !== txid; + return thisTxid !== txid; default: return false; } From f7fa402a57eee1b06e7be3b545b906b5e11d682e Mon Sep 17 00:00:00 2001 From: kranian Date: Mon, 21 Oct 2019 16:46:18 +0900 Subject: [PATCH 51/77] :bug: gray theme fixed - active speed bar mouse hover font fixed --- src/Theme.css | 7 ++++++- .../XlogFlow/XlogFlowContent/XlogFlowContent.js | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Theme.css b/src/Theme.css index 0ccbd08..f9ffb76 100644 --- a/src/Theme.css +++ b/src/Theme.css @@ -362,4 +362,9 @@ html.theme-gray, .theme-gray body { .theme-gray div.xlog-flow-content > div.contents > div.type > div.info{ color : #333; } - +.theme-gray div.xlog-flow-content > div.contents > div.error > div.info{ + color : red; +} +.theme-gray div.active-speed-content > div.row:hover{ + color : #333; +} diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js index 94723c7..95b96a5 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlowContent/XlogFlowContent.js @@ -89,7 +89,7 @@ class XlogFlowContent extends Component { if(target === 'txid') { const {txid, endTime} = this.props.content; const {thisTxid} = this.props; - if(thisTxid !== txid) { + if(thisTxid !== txid && this.isTxFlow()) { this.props.txBtnClick({txid: txid, endTime: endTime}); } } From 8f26766205b575b9f85693906d56f52033a1e8e3 Mon Sep 17 00:00:00 2001 From: kranian Date: Tue, 22 Oct 2019 18:11:31 +0900 Subject: [PATCH 52/77] :zap: brush adding, line chart graph type id adding --- src/components/Paper/LineChart/LineChart.js | 51 +++++++++++++-------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/src/components/Paper/LineChart/LineChart.js b/src/components/Paper/LineChart/LineChart.js index dc5003f..ce12337 100644 --- a/src/components/Paper/LineChart/LineChart.js +++ b/src/components/Paper/LineChart/LineChart.js @@ -449,7 +449,7 @@ class LineChart extends Component { for(const obj of this.props.objects) { for (let counterKey in this.state.counters) { let areaClass = "area-" + obj.objHash + "-" + this.replaceName(counterKey); - this.graph.svg.selectAll("path." + areaClass) + this.graph.line.selectAll("path." + areaClass) .transition() .delay(100) .remove(); @@ -460,7 +460,7 @@ class LineChart extends Component { for(const obj of this.props.objects) { for (let counterKey in this.state.counters) { let lineClass = "line-" + obj.objHash + "-" + this.replaceName(counterKey); - this.graph.svg.selectAll("path." + lineClass) + this.graph.line.selectAll("path." + lineClass) .transition() .delay(100) .remove(); @@ -471,7 +471,7 @@ class LineChart extends Component { removeObjectLine = (obj, counterKey) => { let pathClass = "line-" + obj.objHash + "-" + this.replaceName(counterKey); - let path = this.graph.svg.selectAll("path." + pathClass); + let path = this.graph.line.selectAll("path." + pathClass); // 데이터 삭제 let counters = Object.assign({}, this.state.counters); @@ -502,7 +502,7 @@ class LineChart extends Component { removeObjectLineOnly = (obj, counterKey) => { let pathClass = "line-" + obj.objHash + "-" + this.replaceName(counterKey); - let path = this.graph.svg.selectAll("path." + pathClass); + let path = this.graph.line.selectAll("path." + pathClass); // 라인 그래프 삭제 if (path && path.size() > 0) { @@ -630,11 +630,11 @@ class LineChart extends Component { if (this.props.box.values['chartType'] === "LINE FILL") { let areaClass = "area-" + obj.objHash + "-" + this.replaceName(counterKey); - let area = this.graph.svg.selectAll("path." + areaClass) + let area = this.graph.line.selectAll("path." + areaClass) if (area.size() < 1) { - area = this.graph.svg.insert("path", ":first-child").attr("class", areaClass).style("stroke", color); + area = this.graph.line.insert("path", ":first-child").attr("class", areaClass).style("stroke", color); } let valueArea = d3.area().curve(d3[this.props.config.graph.curve]) @@ -680,10 +680,10 @@ class LineChart extends Component { let pathClass = "line-" + obj.objHash + "-" + this.replaceName(counterKey); - let path = this.graph.svg.selectAll("path." + pathClass); + let path = this.graph.line.selectAll("path." + pathClass); if (path.size() < 1) { - path = this.graph.svg.insert("path", ":first-child").attr("class", pathClass).style("stroke", color); + path = this.graph.line.insert("path", ":first-child").attr("class", pathClass).style("stroke", color); if (this.props.config.graph.color === "instance") { if (this.props.config.colorType === "white") { this.props.setTitle(counterKey, option.title, "#333", option.familyName); @@ -840,7 +840,14 @@ class LineChart extends Component { this.graph.focus.select("line.focus-line").remove(); } }; + zoomBrush = () =>{ + const extent=d3.event.selection; + if(extent) { + // console.log(this.graph.x.invert(extent[0]), this.graph.x.invert(extent[1])); + this.graph.svg.select(".brush").call(this.brush.move, null); + } + }; graphResize = () => { @@ -937,17 +944,17 @@ class LineChart extends Component { this.graph.area = this.graph.svg.append("g") .attr("class", "stack-area") - .attr("clip-path",`url(#area-clip${this.props.box.key})`) - .append("g"); - - + .attr("clip-path",`url(#area-clip${this.props.box.key})`); - this.graph.overlay = this.graph.svg.append("rect").attr("class", "tooltip-overlay").attr("width", this.graph.width).attr("height", this.graph.height); + this.graph.line = this.graph.svg.append("g") + .attr("class", "line-plot") + .attr("clip-path",`url(#area-clip${this.props.box.key})`); + this.graph.overlay = this.graph.svg.append("g").append("rect").attr("class", "tooltip-overlay").attr("width", this.graph.width).attr("height", this.graph.height); - this.graph.overlay.on("mouseover", function () { + this.graph.svg.on("mouseover", function () { let layer = that.refs.lineChartRoot.parentNode.parentNode.parentNode.parentNode.parentNode; layer.style.zIndex = 9; @@ -984,7 +991,7 @@ class LineChart extends Component { //that.props.showTooltip(); }); - this.graph.overlay.on("mouseout",() =>{ + this.graph.svg.on("mouseout",() =>{ let layer = this.refs.lineChartRoot.parentNode.parentNode.parentNode.parentNode.parentNode; layer.style.zIndex = 5; @@ -1005,7 +1012,7 @@ class LineChart extends Component { return d.time; }).left; - this.graph.overlay.on("dblclick",()=>{ + this.graph.svg.on("dblclick",()=>{ if(!this.props.timeFocus.keep){ //toggle //tooltip hidel @@ -1021,7 +1028,7 @@ class LineChart extends Component { ); }); - this.graph.overlay.on("mousemove", function () { + this.graph.svg.on("mousemove", function () { @@ -1030,7 +1037,6 @@ class LineChart extends Component { let xPos = d3.mouse(this)[0]; let yPos = d3.mouse(this)[1]; - if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) { let box = that.refs.lineChart.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode; if (window.getComputedStyle) { @@ -1102,6 +1108,7 @@ class LineChart extends Component { if (tooltip && tooltip.lines) { for (let i = 0; i < tooltip.lines.length; i++) { + if (!isNaN(tooltip.lines[i].value)) { let circle = that.graph.focus.select("circle." + tooltip.lines[i].circleKey).style('display','block'); if (circle.size() > 0) { @@ -1123,6 +1130,14 @@ class LineChart extends Component { }); + + //-- brush + // Add the brush feature using the d3.brush function + this.brush = d3.brushX() + .extent([[0, 0], [this.graph.width, this.graph.height]]) + .on("end", this.zoomBrush); + this.graph.svg.append("g").attr("class", "brush").call(this.brush); + this.graphAxis(this.graph.width, this.graph.height, true); }; From ce71a57ffc735603302fc001962a636e1aefe9d9 Mon Sep 17 00:00:00 2001 From: "yeosong.heo" Date: Wed, 23 Oct 2019 01:42:52 +0900 Subject: [PATCH 53/77] =?UTF-8?q?line=20chart=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0=20=201.=20=ED=83=80=EC=9E=84=20=ED=8F=AC?= =?UTF-8?q?=EC=BB=A4=EC=8A=A4=20=EB=A7=88=EC=9A=B0=EC=8A=A4=20=EC=95=A1?= =?UTF-8?q?=EC=85=98=20=EB=B3=80=EA=B2=BD=20(=20=EC=9D=B4=EC=A0=84=20?= =?UTF-8?q?=EB=A7=88=EC=9A=B0=EC=8A=A4=20=EC=99=BC=EC=AA=BD=20=EB=8D=94?= =?UTF-8?q?=EB=B8=94=ED=81=B4=EB=A6=AD=20,=20right=20=ED=81=B4=EB=A6=AD)?= =?UTF-8?q?=20=202.=20=EC=A4=8C=EC=9D=B8=20,=EC=A4=8C=EC=95=84=EC=9B=83=20?= =?UTF-8?q?=EC=95=A1=EC=85=98=20=EC=B6=94=EA=B0=80=20=20=20-=20=EC=8B=A4?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=EC=9D=98=20=EA=B2=BD=EC=9A=B0=20=20=20=20=20?= =?UTF-8?q?=20ZOOM=20IN=20=EC=95=A1=EC=85=98=EB=8A=94=20=EA=B3=BC=EA=B1=B0?= =?UTF-8?q?=20=EC=8B=9C=EA=B0=84=20=EC=A1=B0=ED=9A=8C=20=20=20-=20?= =?UTF-8?q?=EB=B9=84=EC=8B=A4=EC=8B=9C=EA=B0=84=EC=9D=98=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0=20=20=20=20=20=20ZOOM=20IN=20=20(=EB=A7=88=EC=9A=B0?= =?UTF-8?q?=EC=8A=A4=20=EB=93=9C=EB=9E=98=EA=B7=B8)=20=20=20=20=20=20ZOOM?= =?UTF-8?q?=20OUT=20(=EB=A7=88=EC=9A=B0=EC=8A=A4=20=EB=8D=94=EB=B8=94?= =?UTF-8?q?=ED=81=B4=EB=A6=AD)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Paper/LineChart/LineChart.js | 115 +++++++++++++++----- 1 file changed, 88 insertions(+), 27 deletions(-) diff --git a/src/components/Paper/LineChart/LineChart.js b/src/components/Paper/LineChart/LineChart.js index ce12337..6844342 100644 --- a/src/components/Paper/LineChart/LineChart.js +++ b/src/components/Paper/LineChart/LineChart.js @@ -7,8 +7,14 @@ import * as _ from "lodash"; import ServerDate from "../../../common/ServerDate"; import InstanceColor from "../../../common/InstanceColor"; import numeral from "numeral"; -import {setTimeFocus} from "../../../actions"; - +import { + setRangeDateHoursMinutes, + setRealTimeRangeStepValue, + setRealTimeValue, + setSearchCondition, + setTimeFocus +} from "../../../actions"; +import moment from 'moment'; class LineChart extends Component { @@ -31,6 +37,7 @@ class LineChart extends Component { startTime: (new ServerDate()).getTime() - (1000 * 60 * 10), endTime: (new ServerDate()).getTime(), timeFormat: "%H:%M", + timeFormatSec: "%H:%M:%S", fullTimeFormat: "%Y-%m-%d %H:%M:%S", xAxisWidth: 70, yAxisHeight: 30, @@ -135,7 +142,7 @@ class LineChart extends Component { this.lastCountersTime = nextProps.time; let endTime = nextProps.time; - let startTime = nextProps.time - (1000 * 60 * 10); + let startTime = nextProps.time - (1000 * 60 * 10); //- realtime for (let i = 0; i < nextProps.box.option.length; i++) { let counterKey = nextProps.box.option[i].counterKey; @@ -375,7 +382,17 @@ class LineChart extends Component { leftAxisFormat = (d) => { return numeral(d).format('0.0a'); }; - + setAnimation(svg){ + const {realTime} = this.props.range; + return realTime ? svg : svg.transition().duration(500); + } + getTimeFormat(){ + if( this.state.endTime-this.state.startTime < (60000 * 5)){ + return this.graph.timeFormatSec; + } else{ + return this.graph.timeFormat; + } + } graphAxis = (width, height, init) => { this.graph.x = d3.scaleTime().range([0, width]); this.graph.y = d3.scaleLinear().range([height, 0]); @@ -398,8 +415,10 @@ class LineChart extends Component { this.graph.svg.insert("g", ":first-child").attr("class", "axis-y").call(d3.axisLeft(this.graph.y).tickFormat(this.leftAxisFormat).ticks(yAxisCount)); this.graph.svg.insert("g", ":first-child").attr("class", "grid-y").style("stroke-dasharray", "5 5").style("opacity", this.graph.opacity).call(d3.axisLeft(this.graph.y).tickSize(-this.graph.width).tickFormat("").ticks(yAxisCount)); } else { - this.graph.svg.select(".axis-x").call(d3.axisBottom(this.graph.x).tickFormat(d3.timeFormat(this.graph.timeFormat)).ticks(xAxisCount)); - this.graph.svg.select(".grid-x").call(d3.axisBottom(this.graph.x).tickSize(-this.graph.height).tickFormat("").ticks(xAxisCount)); + this.setAnimation(this.graph.svg.select(".axis-x")) + .call(d3.axisBottom(this.graph.x).tickFormat(d3.timeFormat(this.getTimeFormat())).ticks(xAxisCount)); + this.setAnimation(this.graph.svg.select(".grid-x")) + .call(d3.axisBottom(this.graph.x).tickSize(-this.graph.height).tickFormat("").ticks(xAxisCount)); } if (init) { @@ -519,7 +538,7 @@ class LineChart extends Component { }; - drawGroupObjectLine=(thisOption,counterKey) => { + drawStackArea=(thisOption,counterKey) => { let instanceMetricCount = {}; const color = {}; @@ -730,10 +749,11 @@ class LineChart extends Component { }); if (!this.props.filterMap[obj.objHash]) { - path.data([that.state.counters[counterKey]]).attr("d", valueLine).style("stroke-width", this.props.config.graph.width).style("opacity", 0); + this.setAnimation(path.data([that.state.counters[counterKey]])).attr("d", valueLine).style("stroke-width", this.props.config.graph.width).style("opacity", 0); } else { - path.data([that.state.counters[counterKey]]).attr("d", valueLine).style("stroke-width", this.props.config.graph.width).style("opacity", this.props.config.graph.opacity); + this.setAnimation(path.data([that.state.counters[counterKey]])).attr("d", valueLine).style("stroke-width", this.props.config.graph.width).style("opacity", this.props.config.graph.opacity); } + }; drawTimer = null; @@ -759,7 +779,7 @@ class LineChart extends Component { this.removeObjectLine(this.props.objects[i], counterKey); } } else if(this.chartType ==='STACK AREA') { - this.drawGroupObjectLine(thisOption,counterKey); + this.drawStackArea(thisOption,counterKey); } else { for (let i = 0; i < this.props.objects.length; i++) { const obj = that.props.objects[i]; @@ -794,8 +814,9 @@ class LineChart extends Component { drawTimeFocus=(isFixed=false)=>{ + if( isFixed && !this.state.noData){ - if( Object.keys(this.state.counters).map(k => this.state.counters[k][0].time).filter( t => this.props.timeFocus.time > t ).length ) { + if( Object.keys(this.state.counters).map(k => this.state.counters[k][0] ? this.state.counters[k][0].time : null).filter( t => this.props.timeFocus.time > t ).length ) { let hoverLine = this.graph.focus.selectAll("line.focus-line"); hoverLine.attr("x1", (d) => this.graph.x(d)) .attr("x2", (d) => this.graph.x(d)); @@ -842,9 +863,41 @@ class LineChart extends Component { }; zoomBrush = () =>{ const extent=d3.event.selection; + const {realTime} = this.props.range; if(extent) { - // console.log(this.graph.x.invert(extent[0]), this.graph.x.invert(extent[1])); this.graph.svg.select(".brush").call(this.brush.move, null); + const endTime = this.graph.x.invert(extent[1]); + const startTime = this.graph.x.invert(extent[0]); + if(realTime) { + //- 조회 + const startDate= moment(startTime); + this.props.setRealTimeRangeStepValue(false, this.props.range.longTerm, + this.props.range.value, + this.props.config.range.shortHistoryRange, this.props.config.range.shortHistoryStep); + this.props.setRangeDateHoursMinutes(startDate, startDate.hours(), startDate.minutes()); + this.props.setSearchCondition(startTime.valueOf(), endTime.valueOf(), new Date().getTime()); + + }else{ + //- 확대 + this.setState({ + startTime : startTime.getTime(), + endTime : endTime.getTime() + }); + } + }else{ + const {time,countersHistoryTo,countersHistoryFrom} = this.props; + if(realTime){ + this.setState({ + startTime : time - (1000*60*10), + endTime : time + }); + }else{ + this.setState({ + startTime : countersHistoryFrom, + endTime : countersHistoryTo + }); + } + } }; @@ -1012,19 +1065,22 @@ class LineChart extends Component { return d.time; }).left; - this.graph.svg.on("dblclick",()=>{ - if(!this.props.timeFocus.keep){ - //toggle - //tooltip hidel - that.graph.focus.select("line.x-hover-line").style("display","none"); - that.graph.focus.selectAll("circle").style("display","none"); - that.props.hideTooltip(); - } - this.props.setTimeFocus( - this.props.timeFocus.active, - this.props.timeFocus.time, - this.props.timeFocus.id, - !this.props.timeFocus.keep + this.graph.svg.on("contextmenu",()=>{ + // console.log(d3.event.which); + d3.event.preventDefault(); + // e.preventDefault(); + if(!this.props.timeFocus.keep){ + //toggle + //tooltip hidel + that.graph.focus.select("line.x-hover-line").style("display","none"); + that.graph.focus.selectAll("circle").style("display","none"); + that.props.hideTooltip(); + } + this.props.setTimeFocus( + this.props.timeFocus.active, + this.props.timeFocus.time, + this.props.timeFocus.id, + !this.props.timeFocus.keep ); }); @@ -1184,13 +1240,18 @@ let mapStateToProps = (state) => { objects: state.target.objects, config: state.config, filterMap: state.target.filterMap, - timeFocus: state.timeFocus + timeFocus: state.timeFocus, + range: state.range, }; }; let mapDispatchToProps = (dispatch) => { return { - setTimeFocus: (active, time, boxKey,keep) => dispatch(setTimeFocus(active, time, boxKey,keep)) + setTimeFocus: (active, time, boxKey,keep) => dispatch(setTimeFocus(active, time, boxKey,keep)), + setRealTimeValue: (realTime, longTerm, value) => dispatch(setRealTimeValue(realTime, longTerm, value)), + setRangeDateHoursMinutes: (date, hours, minutes) => dispatch(setRangeDateHoursMinutes(date, hours, minutes)), + setRealTimeRangeStepValue: (realTime, longTerm, value, range, step) => dispatch(setRealTimeRangeStepValue(realTime, longTerm, value, range, step)), + setSearchCondition: (from, to, time) => dispatch(setSearchCondition(from, to, time)), }; }; From 22bffa8f31000de0bec0f6c9241e289e6b26dca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9A=A9=EC=B0=AC?= Date: Wed, 23 Oct 2019 11:10:45 +0900 Subject: [PATCH 54/77] Update ProfileList Order Icon --- .../XLog/Profiler/ProfileList/ProfileList.js | 46 +++++++++++++------ 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/src/components/Paper/XLog/Profiler/ProfileList/ProfileList.js b/src/components/Paper/XLog/Profiler/ProfileList/ProfileList.js index 5bed878..e28c725 100644 --- a/src/components/Paper/XLog/Profiler/ProfileList/ProfileList.js +++ b/src/components/Paper/XLog/Profiler/ProfileList/ProfileList.js @@ -159,23 +159,24 @@ const xlogTypes = { }; class ProfileList extends Component { - + state = { + layoutOrder: layout.map(d=>({...d, order:"no"})) + }; dateFormat = null; fullTimeFormat = null; - componentDidMount() { + componentDidMount() { this.dateFormat = this.props.config.dateFormat; this.fullTimeFormat = this.props.config.dateFormat + " " + this.props.config.timeFormat; } sortTemp = null; - // data sorting onSort(event, type, sortKey) { const data = this.props.xlogs; - + const { layoutOrder } = this.state; if (type === "number" || type === "ms" || type === "kbytes") { - + if (this.sortTemp !== sortKey) { data.sort((a, b) => a[sortKey].localeCompare(b[sortKey], 'en-US', { numeric: true, sensitivity: 'base' })); this.sortTemp = sortKey; @@ -185,17 +186,21 @@ class ProfileList extends Component { } }else{ - + if (this.sortTemp !== sortKey) { data.sort((a, b) => a[sortKey].localeCompare(b[sortKey])); this.sortTemp = sortKey; - }else{ + }else{ data.sort((a, b) => b[sortKey].localeCompare(a[sortKey])); this.sortTemp = null; } - } - - this.setState({data}); + } + const isDesc = this.sortTemp !== sortKey; + this.setState({data, layoutOrder: layoutOrder.map( + d => d.key === sortKey + ? { ...d, order: isDesc ? "asc" : "desc"} + : { ...d, order: "no"} + )}); } getRow = (row, i) => { @@ -219,8 +224,14 @@ class ProfileList extends Component { }; getHeader = () => { - return layout.map((meta, j) => { - return this.onSort(e, meta.type, meta.key)}>{meta.name} + const { layoutOrder } = this.state; + return layout.map((meta, key) => { + const [column] = layoutOrder.filter(d => d.key === meta.key); + if (column.order === "asc") + return this.onSort(e, meta.type, meta.key)}>{meta.name} + if (column.order === "desc") + return this.onSort(e, meta.type, meta.key)}>{meta.name} + return this.onSort(e, meta.type, meta.key)}>{meta.name} }); }; @@ -233,13 +244,22 @@ class ProfileList extends Component { let rowClass = (xlog.error ? 'error' : ''); const xtype = xlogTypes[xlog.xlogType]; rowClass += xtype && xtype >= 2 && xtype <= 4 ? ' async' : ''; - return
{this.getRow(xlog, i)}
; })}
); } + componentWillReceiveProps(nextProps) { + if(nextProps.xlogs !== this.props.xlogs){ + const { layoutOrder } = this.state; + this.setState({ + layoutOrder: layoutOrder.map( + d => ({...d, order:"no"}) + ) + }); + } + } } let mapStateToProps = (state) => { From da7deffd1f81da08cd5a87656d8fe46ff20a6231 Mon Sep 17 00:00:00 2001 From: kranian Date: Wed, 23 Oct 2019 11:13:16 +0900 Subject: [PATCH 55/77] =?UTF-8?q?=ED=83=80=EC=9E=84=20=ED=8F=AC=EC=BB=A4?= =?UTF-8?q?=EC=8A=A4=20=EB=9D=BC=EC=9D=B8=20=EC=BB=AC=EB=9F=AC=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Paper/LineChart/LineChart.css | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/components/Paper/LineChart/LineChart.css b/src/components/Paper/LineChart/LineChart.css index 9c1b337..be93620 100644 --- a/src/components/Paper/LineChart/LineChart.css +++ b/src/components/Paper/LineChart/LineChart.css @@ -41,14 +41,12 @@ .hover-line { stroke: #6F257F; - stroke-width: 1px; - stroke-dasharray: 3, 3; - opacity: 0.5; + stroke-width: 2px; + opacity: 0.9; } .focus-hover-line { stroke: #e03d60; - stroke-width: 3px; - stroke-dasharray: 3, 3; + stroke-width: 2px; opacity: 0.8; } .tooltip-focus circle { From 6164d87c030e83398690f35f3877910a2bcdca5a Mon Sep 17 00:00:00 2001 From: kranian Date: Wed, 23 Oct 2019 14:50:26 +0900 Subject: [PATCH 56/77] stroke-dasharray size interval change --- src/components/Paper/LineChart/LineChart.js | 4 ++-- src/components/Paper/XLog/XLog.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/Paper/LineChart/LineChart.js b/src/components/Paper/LineChart/LineChart.js index 6844342..32ba750 100644 --- a/src/components/Paper/LineChart/LineChart.js +++ b/src/components/Paper/LineChart/LineChart.js @@ -413,7 +413,7 @@ class LineChart extends Component { //- y if (init) { this.graph.svg.insert("g", ":first-child").attr("class", "axis-y").call(d3.axisLeft(this.graph.y).tickFormat(this.leftAxisFormat).ticks(yAxisCount)); - this.graph.svg.insert("g", ":first-child").attr("class", "grid-y").style("stroke-dasharray", "5 5").style("opacity", this.graph.opacity).call(d3.axisLeft(this.graph.y).tickSize(-this.graph.width).tickFormat("").ticks(yAxisCount)); + this.graph.svg.insert("g", ":first-child").attr("class", "grid-y").style("stroke-dasharray", "5 2").style("opacity", this.graph.opacity).call(d3.axisLeft(this.graph.y).tickSize(-this.graph.width).tickFormat("").ticks(yAxisCount)); } else { this.setAnimation(this.graph.svg.select(".axis-x")) .call(d3.axisBottom(this.graph.x).tickFormat(d3.timeFormat(this.getTimeFormat())).ticks(xAxisCount)); @@ -423,7 +423,7 @@ class LineChart extends Component { if (init) { this.graph.svg.insert("g", ":first-child").attr("class", "axis-x").attr("transform", "translate(0," + this.graph.height + ")").call(d3.axisBottom(this.graph.x).tickFormat(d3.timeFormat(this.graph.timeFormat)).ticks(xAxisCount)); - this.graph.svg.insert("g", ":first-child").attr("class", "grid-x").style("stroke-dasharray", "5 5").style("opacity", this.graph.opacity).attr("transform", "translate(0," + this.graph.height + ")").call(d3.axisBottom(this.graph.x).tickSize(-this.graph.height).tickFormat("").ticks(xAxisCount)); + this.graph.svg.insert("g", ":first-child").attr("class", "grid-x").style("stroke-dasharray", "5 2").style("opacity", this.graph.opacity).attr("transform", "translate(0," + this.graph.height + ")").call(d3.axisBottom(this.graph.x).tickSize(-this.graph.height).tickFormat("").ticks(xAxisCount)); } else { this.graph.svg.select(".axis-y").transition().duration(500).call(d3.axisLeft(this.graph.y).tickFormat(this.leftAxisFormat).ticks(yAxisCount)); diff --git a/src/components/Paper/XLog/XLog.js b/src/components/Paper/XLog/XLog.js index c8ea993..b4fac88 100644 --- a/src/components/Paper/XLog/XLog.js +++ b/src/components/Paper/XLog/XLog.js @@ -415,10 +415,10 @@ class XLog extends Component { }).ticks(yAxisCount)); // X축 단위 그리드 그리기 - svg.append("g").attr("class", "grid-x").style("stroke-dasharray", "5 5").style("opacity", this.graph.opacity).attr("transform", "translate(0," + this.graph.height + ")").call(d3.axisBottom(this.graph.x).tickSize(-this.graph.height).tickFormat("").ticks(xAxisCount)); + svg.append("g").attr("class", "grid-x").style("stroke-dasharray", "5 2").style("opacity", this.graph.opacity).attr("transform", "translate(0," + this.graph.height + ")").call(d3.axisBottom(this.graph.x).tickSize(-this.graph.height).tickFormat("").ticks(xAxisCount)); // Y축 단위 그리드 그리기 - svg.append("g").attr("class", "grid-y").style("stroke-dasharray", "5 5").style("opacity", this.graph.opacity).call(d3.axisLeft(this.graph.y).tickSize(-this.graph.width).tickFormat("").ticks(yAxisCount)); + svg.append("g").attr("class", "grid-y").style("stroke-dasharray", "5 2").style("opacity", this.graph.opacity).call(d3.axisLeft(this.graph.y).tickSize(-this.graph.width).tickFormat("").ticks(yAxisCount)); this.graph.focus = svg.append("g").attr("class", "tooltip-focus"); From 9cd3cfbc5b80c683d83b36f62d0ba226531d4472 Mon Sep 17 00:00:00 2001 From: kranian Date: Thu, 24 Oct 2019 19:16:41 +0900 Subject: [PATCH 57/77] :construction: line chart rebuild --- src/components/Paper/LineChart/Line.js | 162 +++ .../Paper/LineChart/LineChart-BackUP.js | 1259 +++++++++++++++++ src/components/Paper/LineChart/LineChart.js | 935 +----------- 3 files changed, 1454 insertions(+), 902 deletions(-) create mode 100644 src/components/Paper/LineChart/Line.js create mode 100644 src/components/Paper/LineChart/LineChart-BackUP.js diff --git a/src/components/Paper/LineChart/Line.js b/src/components/Paper/LineChart/Line.js new file mode 100644 index 0000000..4672a51 --- /dev/null +++ b/src/components/Paper/LineChart/Line.js @@ -0,0 +1,162 @@ +import React,{Component} from "react"; +import { + setRangeDateHoursMinutes, + setRealTimeRangeStepValue, + setRealTimeValue, + setSearchCondition, + setTimeFocus +} from "../../../actions"; +import connect from "react-redux/es/connect/connect"; +import {withRouter} from "react-router-dom"; +import * as d3 from "d3"; +import numeral from "numeral"; + +class Line extends Component { + state ={ + svg : null, + }; + + constructor(props) { + super(props); + + } + + + componentWillReceiveProps(nextProps){ + // console.log("first read;;",nextProps.options,this.props.options); + if( nextProps.options !== this.props.options){ + //- init + // this.prepare(null,nextProps.options); + } + }; + + zoomBrush = () => { + const extent = d3.event.selection; + const {realTime} = this.props.range; + }; + + prepare(svg,options){ + const {width,height,margin} = options; + // const {svg}= this.state; + // console.log('',svg,margin,width,height); + // if(!svg) return; + console.log(options); + this.svg = d3.select(svg) + .attr("width", width + margin.left + margin.right) + .attr("height", height + margin.top + margin.bottom); + + this.svg.append("defs") + .append("svg:clipPath") + .attr("id", `area-clip${this.props.box.key}`) + .append("svg:rect") + .attr("x", 0) + .attr("y", 0) + .attr("width", width) + .attr("height", height); + + this.top = this.svg.append("g") + .attr("class", "top-group") + .attr("transform", `translate(${margin.left},${margin.top}")`); + this.focus = this.top.append("g").attr("class", "tooltip-focus"); + + this.area = this.top.append("g") + .attr("class", "stack-area") + .attr("clip-path",`url(#area-clip${this.props.box.key})`); + + this.line = this.top.append("g") + .attr("class", "line-plot") + .attr("clip-path",`url(#area-clip${this.props.box.key})`); + + this.brush = d3.brushX() + .extent([[0, 0], [width, height]]) + .on("end", this.zoomBrush); + + this.top.append("g").attr("class", "brush").call(this.brush); + + //Axis Draw + this.xAxis = d3.scaleTime().range([0, width]); + this.yAxis = d3.scaleLinear().range([height, 0]); + + this.xAxis.domain([this.props.startTime, this.props.endTime]); + this.yAxis.domain([0, options.maxY]); + + let xAxisCount = Math.floor(width / options.xAxisWidth); + if (xAxisCount < 1) { + xAxisCount = 1; + } + let yAxisCount = Math.floor(height / options.yAxisHeight); + if (yAxisCount < 1) { + yAxisCount = 1; + } + + this.top.insert("g", ":first-child").attr("class", "axis-y") + .call(d3.axisLeft(this.yAxis) + .tickFormat((d)=>numeral(d).format('0.0a')).ticks(yAxisCount)); + + this.top.insert("g", ":first-child") + .attr("class", "grid-y") + .style("stroke-dasharray", "5 2") + .style("opacity", options.opacity) + .call(d3.axisLeft(this.yAxis) + .tickSize(-options.width) + .tickFormat("").ticks(yAxisCount)); + + + this.top.insert("g", ":first-child") + .attr("class", "axis-x") + .attr("transform", "translate(0," + height + ")") + .call(d3.axisBottom(this.xAxis) + .tickFormat(d3.timeFormat(options.timeFormat)) + .ticks(xAxisCount)); + this.top.insert("g", ":first-child") + .attr("class", "grid-x") + .style("stroke-dasharray", "5 2") + .style("opacity", options.opacity) + .attr("transform", `translate(0,${options.height})`) + .call(d3.axisBottom(this.xAxis) + .tickSize(-options.height) + .tickFormat("") + .ticks(xAxisCount)); + + }; + + shouldComponentUpdate() { + return false; + }; + + onRef = (ref) => { + this.setState({ svg : d3.select(ref) } ,()=>{ + this.prepare(ref,this.props.options); + }); + }; + + render(){ + return ( + + + ); + }; +} + +let mapStateToProps = (state) => { + return { + objects: state.target.objects, + config: state.config, + filterMap: state.target.filterMap, + timeFocus: state.timeFocus, + range: state.range, + }; +}; + +let mapDispatchToProps = (dispatch) => { + return { + setTimeFocus: (active, time, boxKey,keep) => dispatch(setTimeFocus(active, time, boxKey,keep)), + setRealTimeValue: (realTime, longTerm, value) => dispatch(setRealTimeValue(realTime, longTerm, value)), + setRangeDateHoursMinutes: (date, hours, minutes) => dispatch(setRangeDateHoursMinutes(date, hours, minutes)), + setRealTimeRangeStepValue: (realTime, longTerm, value, range, step) => dispatch(setRealTimeRangeStepValue(realTime, longTerm, value, range, step)), + setSearchCondition: (from, to, time) => dispatch(setSearchCondition(from, to, time)), + }; +}; + +Line = connect(mapStateToProps, mapDispatchToProps)(Line); +export default withRouter(Line); \ No newline at end of file diff --git a/src/components/Paper/LineChart/LineChart-BackUP.js b/src/components/Paper/LineChart/LineChart-BackUP.js new file mode 100644 index 0000000..56e0442 --- /dev/null +++ b/src/components/Paper/LineChart/LineChart-BackUP.js @@ -0,0 +1,1259 @@ +import React, {Component} from 'react'; +import './LineChart.css'; +import {connect} from 'react-redux'; +import {withRouter} from 'react-router-dom'; +import * as d3 from "d3"; +import * as _ from "lodash"; +import ServerDate from "../../../common/ServerDate"; +import InstanceColor from "../../../common/InstanceColor"; +import numeral from "numeral"; +import { + setRangeDateHoursMinutes, + setRealTimeRangeStepValue, + setRealTimeValue, + setSearchCondition, + setTimeFocus +} from "../../../actions"; +import moment from 'moment'; + +class LineChart extends Component { + + lastCountersTime = null; + lastCountersHistoryTime = null; + historyInit = {}; + chartType = "LINE"; + timeFocusId = null; + graph = { + margin: { + top: 15, right: 15, bottom: 25, left: 40 + }, + svg: null, + overlay: null, + width: null, + height: null, + x: null, + y: null, + path: null, + startTime: (new ServerDate()).getTime() - (1000 * 60 * 10), + endTime: (new ServerDate()).getTime(), + timeFormat: "%H:%M", + timeFormatSec: "%H:%M:%S", + fullTimeFormat: "%Y-%m-%d %H:%M:%S", + xAxisWidth: 70, + yAxisHeight: 30, + noData: true, + bisector: null, + currentTooltipTime: null, + opacity : 0.3 + }; + + constructor(props) { + super(props); + this.state = { + counters: {}, + maxY: null, + autoMaxY: null + } + } + + componentDidMount() { + if (this.props.config.colorType === "white") { + this.graph.opacity = 0.3; + } else { + this.graph.opacity = 0.6; + } + + + this.graph.timeFormat = this.props.config.minuteFormat; + this.graph.fullTimeFormat = this.props.config.dateFormat + " " + this.props.config.timeFormat; + this.graphInit(); + } + + shouldComponentUpdate(nextProps, nextState) { + + /*if (this.props.visible && this.props.countersHistoryTimestamp !== nextProps.countersHistoryTimestamp) { + return true; + }*/ + + return !!this.props.visible; + } + + + + componentWillReceiveProps(nextProps) { + let counters = Object.assign({}, this.state.counters); + + + if( nextProps.box.values['chartType'] !== this.chartType){ + //- LINE FILL => LINE Change + if( nextProps.box.values['chartType'] === 'LINE' && this.chartType === 'LINE FILL' ){ + this.removeLineFill(); + } + //- LINE or LINE FILL => STACK; + if( nextProps.box.values['chartType'] === 'STACK AREA' && ( this.chartType === 'LINE' || this.chartType === 'LINE FILL') ){ + this.removeLineFill(); + this.removeLine(); + } + // STACK => LINE or LINE FILL + if( (nextProps.box.values['chartType'] === 'LINE' || nextProps.box.values['chartType'] === 'LINE FILL' ) && ( this.chartType === 'STACK AREA') ){ + this.removeStackFill(); + } + } + + if (nextProps.countersHistory && this.lastCountersHistoryTime !== nextProps.countersHistoryTimestamp) { + + this.lastCountersHistoryTime = nextProps.countersHistoryTimestamp; + for (let i = 0; i < nextProps.box.option.length; i++) { + let counterKey = nextProps.box.option[i].counterKey; + if (nextProps.countersHistory[counterKey]) { + + counters = this.loadHistoryCounter(nextProps.countersHistory, counterKey, nextProps.longTerm); + this.setState({ + counters: counters + }); + } else { + counters[counterKey] = []; + this.setState({ + counters: counters + }); + } + } + + this.graph.maxY = 1; + + this.setMaxY(counters, nextProps.box.option); + + let startTime = (new ServerDate()).getTime() - (1000 * 60 * 10); + let endTime = (new ServerDate()).getTime(); + + if (nextProps.countersHistoryFrom && nextProps.countersHistoryTo) { + endTime = nextProps.countersHistoryTo; + startTime = nextProps.countersHistoryFrom; + } + + this.setState({ + startTime: startTime, + endTime: endTime + }); + } + + if (nextProps.counters && nextProps.time !== this.lastCountersTime) { + + this.lastCountersTime = nextProps.time; + + let endTime = nextProps.time; + let startTime = nextProps.time - (1000 * 60 * 10); //- realtime + + for (let i = 0; i < nextProps.box.option.length; i++) { + let counterKey = nextProps.box.option[i].counterKey; + counters[counterKey] = counters[counterKey] || []; + counters[counterKey].push({ + time: nextProps.time, + data: nextProps.counters[counterKey] + }); + } + + for (let counterKey in counters) { + let overIndex = -1; + for (let i = 0; i < counters[counterKey].length; i++) { + if (counters[counterKey][i].time < startTime) { + overIndex = i; + } else { + break; + } + } + + if (overIndex > -1) { + counters[counterKey].splice(0, overIndex + 1); + } + } + + this.setMaxY(counters, nextProps.box.option); + + let noData = true; + if (Array.isArray(nextProps.box.option)) { + for (let i = 0; i < nextProps.box.option.length; i++) { + let counterKey = nextProps.box.option[i].counterKey; + if (nextProps.counters[counterKey]) { + if (Object.keys(nextProps.counters[counterKey]).length !== 0) { + noData = false; + break; + } + } + + } + } else { + let counterKey = nextProps.box.option.counterKey; + if (Object.keys(nextProps.counters[counterKey]).length !== 0) { + noData = false; + } + } + + this.setState({ + startTime: startTime, + endTime: endTime, + counters: counters, + noData: noData + }); + + } + //- 이전 툴팁이 고정 되었으면 자동 해지 할수 있도록 이벤트 체크 + this.chartType = nextProps.box.values['chartType']; + if(!this.props.timeFocus.keep) { + this.drawTimeFocus(); + } + } + + loadHistoryCounter(countersHistory, counterKey, longTerm) { + let decimalPoint = this.props.config.decimalPoint; + let pow = 1; + + if (decimalPoint > 0) { + pow = Math.pow(10, decimalPoint); + } + + let counters = this.state.counters; + counters[counterKey] = []; + const timeKeyRow = {}; + for (let objHash in countersHistory[counterKey]) { + + let timeList = countersHistory[counterKey][objHash].timeList; + let valueList = countersHistory[counterKey][objHash].valueList; + let unit = countersHistory[counterKey][objHash].unit; + + for (let j = 0; j < timeList.length; j++) { + let row = {}; + let timeUnit = 2000; + if (longTerm) { + timeUnit = 1000 * 60 * 5; + } + row.time = parseInt(timeList[j] / timeUnit, 10) * timeUnit; + row.data = {}; + row.data[objHash] = { + objHash: objHash, + value: Math.round(valueList[j] * pow) / pow, + unit: unit + }; + + if (!timeKeyRow[row.time]) { + timeKeyRow[row.time] = row; + } else { + timeKeyRow[row.time].data[objHash] = { + objHash: objHash, + value: Math.round(valueList[j] * pow) / pow, + unit: unit + }; + } + } + } + + for (const timeKey in timeKeyRow) { + counters[counterKey].push(timeKeyRow[timeKey]); + } + counters[counterKey].sort((a, b) => a.time - b.time); + + return counters; + } + + setMaxY = (counters, option) => { + + + let metricMap = {}; + option.forEach((option) => { + metricMap[option.counterKey] = true; + }); + + let maxY = 0; + if( this.chartType === "STACK AREA"){ + for (let attr in counters) { + if (!metricMap[attr]) { + continue; + } + for (let i = 0; i < counters[attr].length; i++) { + // y축 sum + const sum = Object.keys(counters[attr][i].data) + .map(_key => Number(counters[attr][i].data[_key].value)) + .reduce((acc,cur)=> acc+cur,0); + if( sum > maxY){ + maxY = sum; + } + } + } + + }else { + for (let attr in counters) { + if (!metricMap[attr]) { + continue; + } + + for (let i = 0; i < counters[attr].length; i++) { + for (let hash in counters[attr][i].data) { + if (Array.isArray(counters[attr][i].data[hash].value)) { + for (let j = 0; j < counters[attr][i].data[hash].value.length; j++) { + // line chart + if (Number(counters[attr][i].data[hash].value[j]) > maxY) { + maxY = Number(counters[attr][i].data[hash].value[j]); + } + } + } else { + if (Number(counters[attr][i].data[hash].value) > maxY) { + maxY = Number(counters[attr][i].data[hash].value); + } + } + } + } + } + } + + if (!maxY || maxY < 1) { + maxY = 1; + } + + this.graph.autoMaxY = maxY; + + if (!this.graph.maxY) { + this.graph.maxY = maxY * 1.2; + } + + if (this.graph.autoMaxY > this.graph.maxY) { + this.graph.maxY = this.graph.autoMaxY * 1.2; + } + + + }; + + removeObjLine(prevList, currentList) { + // 제외된 인스턴스 찾기 + let currentInstanceMap = {}; + if (currentList && currentList.length > 0) { + for (let i = 0; i < currentList.length; i++) { + currentInstanceMap[currentList[i].objHash] = true; + } + } + + if (prevList && prevList.length > 0) { + for (let i = 0; i < prevList.length; i++) { + if (!currentInstanceMap[prevList[i].objHash]) { + + for (let counterKey in this.state.counters) { + + let thisOption = null; + for (let j = 0; j < this.props.box.option.length; j++) { + if (this.props.box.option[j].counterKey === counterKey) { + thisOption = this.props.box.option[j]; + break; + } + } + + if (thisOption) { + this.removeObjectLineOnly(prevList[i], counterKey); + } + } + } + } + } + } + + componentDidUpdate = (prevProps, prevState) => { + if (prevProps.layoutChangeTime !== this.props.layoutChangeTime) { + if (this.graphResize()) { + this.draw(); + } + } else { + this.graphResize(); + this.draw(); + this.removeObjLine(prevProps.objects, this.props.objects); + } + }; + + + + moveTooltip = () => { + if (this.graph.currentTooltipTime) { + let xPosition = this.graph.x(this.graph.currentTooltipTime); + this.graph.focus.selectAll("circle").attr("cx", xPosition); + + let hoverLine = this.graph.focus.select("line.x-hover-line"); + hoverLine.attr("x1", xPosition); + hoverLine.attr("x2", xPosition); + } + }; + + leftAxisFormat = (d) => { + return numeral(d).format('0.0a'); + }; + setAnimation(svg){ + const {realTime} = this.props.range; + return realTime ? svg : svg.transition().duration(500); + } + getTimeFormat(){ + if( this.state.endTime-this.state.startTime < (60000 * 5)){ + return this.graph.timeFormatSec; + } else{ + return this.graph.timeFormat; + } + } + graphAxis = (width, height, init) => { + this.graph.x = d3.scaleTime().range([0, width]); + this.graph.y = d3.scaleLinear().range([height, 0]); + + this.graph.x.domain([this.state.startTime, this.state.endTime]); + this.graph.y.domain([0, this.graph.maxY]); + + let xAxisCount = Math.floor(this.graph.width / this.graph.xAxisWidth); + if (xAxisCount < 1) { + xAxisCount = 1; + } + + let yAxisCount = Math.floor(this.graph.height / this.graph.yAxisHeight); + if (yAxisCount < 1) { + yAxisCount = 1; + } + + //- y + if (init) { + this.graph.svg.insert("g", ":first-child").attr("class", "axis-y").call(d3.axisLeft(this.graph.y).tickFormat(this.leftAxisFormat).ticks(yAxisCount)); + this.graph.svg.insert("g", ":first-child").attr("class", "grid-y").style("stroke-dasharray", "5 2").style("opacity", this.graph.opacity).call(d3.axisLeft(this.graph.y).tickSize(-this.graph.width).tickFormat("").ticks(yAxisCount)); + } else { + this.setAnimation(this.graph.svg.select(".axis-x")) + .call(d3.axisBottom(this.graph.x).tickFormat(d3.timeFormat(this.getTimeFormat())).ticks(xAxisCount)); + this.setAnimation(this.graph.svg.select(".grid-x")) + .call(d3.axisBottom(this.graph.x).tickSize(-this.graph.height).tickFormat("").ticks(xAxisCount)); + } + + if (init) { + this.graph.svg.insert("g", ":first-child").attr("class", "axis-x").attr("transform", "translate(0," + this.graph.height + ")").call(d3.axisBottom(this.graph.x).tickFormat(d3.timeFormat(this.graph.timeFormat)).ticks(xAxisCount)); + this.graph.svg.insert("g", ":first-child").attr("class", "grid-x").style("stroke-dasharray", "5 2").style("opacity", this.graph.opacity).attr("transform", "translate(0," + this.graph.height + ")").call(d3.axisBottom(this.graph.x).tickSize(-this.graph.height).tickFormat("").ticks(xAxisCount)); + + } else { + this.graph.svg.select(".axis-y").transition().duration(500).call(d3.axisLeft(this.graph.y).tickFormat(this.leftAxisFormat).ticks(yAxisCount)); + this.graph.svg.select(".grid-y").transition().duration(500).call(d3.axisLeft(this.graph.y).tickSize(-this.graph.width).tickFormat("").ticks(yAxisCount)); + } + + + }; + + manualTooltipHide = ( ) =>{ + + let layer = this.refs.lineChartRoot.parentNode.parentNode.parentNode.parentNode.parentNode; + layer.style.zIndex = 5; + this.graph.focus.selectAll("circle").style("display", "none"); + + let hoverLine = this.graph.focus.select("line.x-hover-line"); + if (hoverLine.size() > 0) { + hoverLine.style("display", "none"); + } + this.props.hideTooltip(); + }; + + + replaceAll(str, searchStr, replaceStr) { + return str.split(searchStr).join(replaceStr); + } + + replaceName (name) { + if (name) { + return this.replaceAll(this.replaceAll(name, "%", "_PCT_"), '$', '_DOLLAR_'); + } + return name; + } + removeStackFill = () =>{ + this.graph.svg.select('g.stack-area').selectAll('path.line') + .transition() + .delay(100) + .remove(); + }; + + removeLineFill = () =>{ + for(const obj of this.props.objects) { + for (let counterKey in this.state.counters) { + let areaClass = "area-" + obj.objHash + "-" + this.replaceName(counterKey); + this.graph.line.selectAll("path." + areaClass) + .transition() + .delay(100) + .remove(); + } + } + }; + removeLine = () =>{ + for(const obj of this.props.objects) { + for (let counterKey in this.state.counters) { + let lineClass = "line-" + obj.objHash + "-" + this.replaceName(counterKey); + this.graph.line.selectAll("path." + lineClass) + .transition() + .delay(100) + .remove(); + } + } + }; + + + removeObjectLine = (obj, counterKey) => { + let pathClass = "line-" + obj.objHash + "-" + this.replaceName(counterKey); + let path = this.graph.line.selectAll("path." + pathClass); + + // 데이터 삭제 + let counters = Object.assign({}, this.state.counters); + if (counters[counterKey]) { + delete counters[counterKey]; + } + delete this.state.counters[counterKey]; + this.setState({ + counters: counters + }); + + // 라인 그래프 삭제 + if (path && path.size() > 0) { + path.remove(); + } + + // 툴팁 그래프 삭제 + let circleKey = "circle-" + obj.objHash + "_" + this.replaceName(counterKey); + let circle = this.graph.focus.selectAll("circle." + circleKey); + + if (circle.size() > 0) { + circle.remove(); + } + + // 제목 삭제 + this.props.removeTitle(counterKey); + }; + + removeObjectLineOnly = (obj, counterKey) => { + let pathClass = "line-" + obj.objHash + "-" + this.replaceName(counterKey); + let path = this.graph.line.selectAll("path." + pathClass); + + // 라인 그래프 삭제 + if (path && path.size() > 0) { + path.remove(); + } + + // 툴팁 그래프 삭제 + let circleKey = "circle-" + obj.objHash + "_" + this.replaceName(counterKey); + let circle = this.graph.focus.selectAll("circle." + circleKey); + + if (circle.size() > 0) { + circle.remove(); + } + + }; + + drawStackArea=(thisOption,counterKey) => { + + let instanceMetricCount = {}; + const color = {}; + //- instance color making + for (const attr in this.props.objects) { + const _obj = this.props.objects[attr]; + if (_obj.objFamily === thisOption.familyName) { + if (!instanceMetricCount[_obj.objHash]) { + instanceMetricCount[_obj.objHash] = 0; + } + if (this.props.config.graph.color === "metric") { + const _cl = InstanceColor.getMetricColor(thisOption.counterKey, this.props.config.colorType); + color[_obj.objHash] = _cl; + } else { + const _cl = InstanceColor.getInstanceColors(this.props.config.colorType)[_obj.objHash][(instanceMetricCount[_obj.objHash]++) % 5]; + color[_obj.objHash] = _cl; + } + } + } + //- instance data flat data making + const stackData = _(this.state.counters[counterKey]) + .map((d) => { + const _r = Object.keys(d.data).map(key => { + const _keyValue = []; + _keyValue['objHash'] = d.data[key].objHash; + _keyValue['time'] = d.time; + _keyValue['value'] = Number(d.data[key].value); + _keyValue['color'] = color[d.data[key].objHash]; + return _keyValue; + }); + return _r; + }).flatMapDepth().value(); + //- 인스턴스 별 데이터로 변환 + let ld = d3.nest().key(d => d.objHash).entries(stackData); + const _sort = []; + + //- 인스턴스 순서 정렬 + for (const attr in this.props.objects) { + const _obj = this.props.objects[attr]; + const _find = _.findIndex(ld, (o) => o.key === _obj.objHash); + if(_find > -1 ){ + _sort.push(ld[_find]); + } + + } + //- 인스턴스 그리기 + const area = d3.area().curve(d3[this.props.config.graph.curve]) + .x(d =>{ + return this.graph.x(d[0]); + }) + .y0(d => this.graph.y(d[2])) + .y1(d => this.graph.y(d[1])); + + if (this.props.config.graph.break === "Y") { + area.defined((d)=>{ + return !isNaN(d[0]) && !isNaN(d[1]) && !isNaN(d[2]); + }) + } + + //- 시간 별 Y축 데이터 어그리게이션 + let pre = {}; + //- 차트 갱신 + let paintGroup = this.graph.area.selectAll("path.line") + .data(_sort) + .attr("d",(d)=> { + const _d = _.map(d.values,(_node) =>{ + const _key = _node.time; + const pre_v = pre[_key] ? pre[_key] : 0; + const next_v = pre_v + Number(_node.value); + pre[_key] = next_v; + return [_node.time,next_v,pre_v]; + }); + return area(_d); + }); + + //- 차트 생성 + paintGroup.enter() + .append('path') + .attr("d",(d)=> { + const _d = _.map(d.values,(_node) =>{ + const _key = _node.time; + const pre_v = pre[_key] ? pre[_key] : 0; + const next_v = pre_v + _node.value; + pre[_key] = next_v; + return [_node.time,next_v,pre_v]; + }); + return area(_d); + }) + .attr('class',(d)=> `line ${d.key}` ) + .attr('data-col-name', (d)=> d.key) + .style("fill", (d)=> { + return color[d.key]; + }) + .attr("fill-opacity", this.props.config.graph.fillOpacity) + .attr("stroke",(d) =>{ + return color[d.key]; + }) + .style("stroke-width", this.props.config.graph.width) + .style("opacity", this.props.config.graph.opacity); + + //- 차트 갱신 후 데이터 삭제 + paintGroup.exit().remove(); + }; + + + drawObjectLine = (obj, option, counterKey, color) => { + const that = this; + if (this.props.box.values['chartType'] === "LINE FILL") { + + let areaClass = "area-" + obj.objHash + "-" + this.replaceName(counterKey); + let area = this.graph.line.selectAll("path." + areaClass) + + + if (area.size() < 1) { + area = this.graph.line.insert("path", ":first-child").attr("class", areaClass).style("stroke", color); + } + + let valueArea = d3.area().curve(d3[this.props.config.graph.curve]) + .x(function (d) { + return that.graph.x(d.time); + }) + .y0(that.graph.height) + .y1(function (counter) { + let objData = counter.data[obj.objHash]; + if (objData) { + return that.graph.y(objData.value); + } else { + return that.graph.y(0); + } + }); + + + if (this.props.config.graph.break === "Y") { + valueArea.defined(function (d) { + let objData = d.data ? d.data[obj.objHash] : null; + return objData && !isNaN(d.time) && !isNaN(objData.value) && !isNaN(that.graph.y(objData.value)); + }) + } + + + if (!this.props.filterMap[obj.objHash]) { + area.data([that.state.counters[counterKey]]) + .attr("d", valueArea) + .style("fill", color) + .style("opacity", 0) + .transition() + .delay(100); + } else { + area.data([that.state.counters[counterKey]]) + .attr("d", valueArea) + .style("fill", color) + .style("opacity", this.props.config.graph.fillOpacity) + .transition() + .delay(100); + } + } + + + + let pathClass = "line-" + obj.objHash + "-" + this.replaceName(counterKey); + let path = this.graph.line.selectAll("path." + pathClass); + + if (path.size() < 1) { + path = this.graph.line.insert("path", ":first-child").attr("class", pathClass).style("stroke", color); + if (this.props.config.graph.color === "instance") { + if (this.props.config.colorType === "white") { + this.props.setTitle(counterKey, option.title, "#333", option.familyName); + } else { + this.props.setTitle(counterKey, option.title, "white", option.familyName); + } + } else { + this.props.setTitle(counterKey, option.title, color, option.familyName); + } + } else { + path.style("stroke", color); + if (this.props.config.graph.color === "instance") { + if (this.props.config.colorType === "white") { + this.props.setTitle(counterKey, option.title, "#333", option.familyName); + } else { + this.props.setTitle(counterKey, option.title, "white", option.familyName); + } + } else { + this.props.setTitle(counterKey, option.title, color, option.familyName); + } + + let circleKey = "circle-" + obj.objHash + "_" + this.replaceName(counterKey); + that.graph.focus.select("circle." + circleKey).attr("stroke", color); + } + + let valueLine = d3.line().curve(d3[this.props.config.graph.curve]); + + if (this.props.config.graph.break === "Y") { + valueLine.defined(function (d) { + let objData = d.data ? d.data[obj.objHash] : null; + return objData && !isNaN(d.time) && !isNaN(objData.value) && !isNaN(that.graph.y(objData.value)); + }) + } + + valueLine.x(function (d) { + return that.graph.x(d.time); + }).y(function (counter) { + let objData = counter.data[obj.objHash]; + if (objData) { + return that.graph.y(objData.value); + } else { + return that.graph.y(0); + } + }); + + if (!this.props.filterMap[obj.objHash]) { + this.setAnimation(path.data([that.state.counters[counterKey]])).attr("d", valueLine).style("stroke-width", this.props.config.graph.width).style("opacity", 0); + } else { + this.setAnimation(path.data([that.state.counters[counterKey]])).attr("d", valueLine).style("stroke-width", this.props.config.graph.width).style("opacity", this.props.config.graph.opacity); + } + + }; + + drawTimer = null; + + draw = () => { + + let that = this; + if (this.drawTimer) { + clearTimeout(this.drawTimer); + this.drawTimer = null; + } + + this.drawTimer = setTimeout(() => { + if (this.refs.lineChart && this.graph.svg) { + + this.graphAxis(this.graph.width, this.graph.height, false); + if (this.props.objects) { + let instanceMetricCount = {}; + for (let counterKey in this.state.counters) { + let thisOption = that.props.box.option.filter((d) => {return d.counterKey === counterKey})[0]; + if (!thisOption) { + for (let i = 0; i < this.props.objects.length; i++) { + this.removeObjectLine(this.props.objects[i], counterKey); + } + } else if(this.chartType ==='STACK AREA') { + this.drawStackArea(thisOption,counterKey); + } else { + for (let i = 0; i < this.props.objects.length; i++) { + const obj = that.props.objects[i]; + + if (obj.objFamily === thisOption.familyName) { + if (!instanceMetricCount[obj.objHash]) { + instanceMetricCount[obj.objHash] = 0; + } + let color; + if (this.props.config.graph.color === "metric") { + color = InstanceColor.getMetricColor(thisOption.counterKey, this.props.config.colorType); + } else { + color = InstanceColor.getInstanceColors(this.props.config.colorType)[obj.objHash][(instanceMetricCount[obj.objHash]++) % 5]; + } + this.drawObjectLine(obj, thisOption, counterKey, color); + } + } + + } + } + } + } + + this.moveTooltip(); + if(this.props.timeFocus.keep) { + this.drawTimeFocus(true); + + } + }, 200); + + }; + + + drawTimeFocus=(isFixed=false)=>{ + + if( isFixed && !this.state.noData){ + if( Object.keys(this.state.counters).map(k => this.state.counters[k][0] ? this.state.counters[k][0].time : null).filter( t => this.props.timeFocus.time > t ).length ) { + let hoverLine = this.graph.focus.selectAll("line.focus-line"); + hoverLine.attr("x1", (d) => this.graph.x(d)) + .attr("x2", (d) => this.graph.x(d)); + + hoverLine.data([this.props.timeFocus.time]) + .enter() + .append("line") + .attr("class", "focus-line focus-hover-line") + .attr("y1", 0) + .attr("y2", this.graph.height) + .attr("x1", (d) => { + return this.graph.x(d); + }) + .attr("x2", (d) => this.graph.x(d)) + .exit() + .remove(); + }else{ + // 해제 + this.props.setTimeFocus(false,null,null,false); + } + + }else if( !this.state.noData + && this.props.timeFocus.active + && this.props.timeFocus.id !== this.props.box.key) { + let hoverLine = this.graph.focus.selectAll("line.focus-line"); + hoverLine.attr("x1", (d) =>this.graph.x(d)) + .attr("x2", (d) =>this.graph.x(d)); + + hoverLine.data([this.props.timeFocus.time]) + .enter() + .append("line") + .attr("class", "focus-line focus-hover-line") + .attr("y1", 0) + .attr("y2", this.graph.height) + .attr("x1", (d) =>{ + return this.graph.x(d); + }) + .attr("x2", (d) =>this.graph.x(d)) + .exit() + .remove(); + }else{ + this.graph.focus.select("line.focus-line").remove(); + } + }; + zoomBrush = () =>{ + const extent=d3.event.selection; + const {realTime} = this.props.range; + if(extent) { + this.graph.svg.select(".brush").call(this.brush.move, null); + const endTime = this.graph.x.invert(extent[1]); + const startTime = this.graph.x.invert(extent[0]); + if(realTime) { + //- 조회 + const startDate= moment(startTime); + this.props.setRealTimeRangeStepValue(false, this.props.range.longTerm, + this.props.range.value, + this.props.config.range.shortHistoryRange, this.props.config.range.shortHistoryStep); + this.props.setRangeDateHoursMinutes(startDate, startDate.hours(), startDate.minutes()); + this.props.setSearchCondition(startTime.valueOf(), endTime.valueOf(), new Date().getTime()); + + }else{ + //- 확대 + this.setState({ + startTime : startTime.getTime(), + endTime : endTime.getTime() + }); + } + }else{ + const {time,countersHistoryTo,countersHistoryFrom} = this.props; + if(realTime){ + this.setState({ + startTime : time - (1000*60*10), + endTime : time + }); + }else{ + this.setState({ + startTime : countersHistoryFrom, + endTime : countersHistoryTo + }); + } + + } + + }; + + + graphResize = () => { + let resized = false; + let box = this.refs.lineChart.parentNode.parentNode.parentNode.parentNode; + if ((box.offsetWidth - this.graph.margin.left - this.graph.margin.right !== this.graph.width) || (this.graph.height !== box.offsetHeight - this.graph.margin.top - this.graph.margin.bottom - 27)) { + resized = true; + this.graphInit(); + this.manualTooltipHide(); + } + + return resized; + }; + + mouseOverObject = (obj, thisOption, color) => { + let that = this; + let r = 3; + + let circleKey = "circle-" + obj.objHash + "_" + this.replaceName(thisOption.counterKey); + let circle = that.graph.focus.select("circle." + circleKey); + if (circle.size() < 1) { + circle = that.graph.focus.append("circle").attr("class", circleKey).attr("r", r).attr("stroke", color); + } + + if (this.props.filterMap[obj.objHash]) { + circle.style("opacity", 1); + } else { + circle.style("opacity", 0); + } + + }; + + mouseMoveObject = (obj, thisOption, counterKey, dataIndex, color, tooltip) => { + let that = this; + + let circleKey = "circle-" + obj.objHash + "_" + this.replaceName(thisOption.counterKey); + let unit = that.state.counters[counterKey][dataIndex].data[obj.objHash] ? that.state.counters[counterKey][dataIndex].data[obj.objHash].unit : ""; + + let valueOutput = obj.objHash && that.state.counters[counterKey][dataIndex].data[obj.objHash] ? that.state.counters[counterKey][dataIndex].data[obj.objHash].value : null ; + const valueOrigin = obj.objHash && that.state.counters[counterKey][dataIndex].data[obj.objHash] ? that.state.counters[counterKey][dataIndex].data[obj.objHash].value : null ; + if( this.chartType === "STACK AREA" && valueOutput ){ + valueOutput = this.counterSum + valueOutput; + this.counterSum = valueOutput; + } + + if (that.state.counters[counterKey][dataIndex].time) { + if (this.props.filterMap[obj.objHash]) { + tooltip.lines.push({ + instanceName: obj.objName, + circleKey: circleKey, + metricName: thisOption.title, + value: valueOutput ? valueOutput : null, + displayValue: valueOrigin ? numeral(valueOrigin).format(this.props.config.numberFormat) + " " + unit : null, + color: color + }); + } + } else { + that.graph.focus.select("circle." + circleKey).style("display", "none"); + } + + return true; + }; + + graphInit = () => { + + let that = this; + let box = this.refs.lineChart.parentNode.parentNode.parentNode.parentNode; + this.graph.width = box.offsetWidth - this.graph.margin.left - this.graph.margin.right; + this.graph.height = box.offsetHeight - this.graph.margin.top - this.graph.margin.bottom - 27; + + let svg = d3.select(this.refs.lineChart).select("svg"); + if (svg.size() > 0) { + svg.remove(); + } + + this.graph.svg = d3.select(this.refs.lineChart) + .append("svg") + .attr("width", this.graph.width + this.graph.margin.left + this.graph.margin.right) + .attr("height", this.graph.height + this.graph.margin.top + this.graph.margin.bottom); + + d3.select(this.refs.lineChart).select("svg").append("defs") + .append("svg:clipPath") + .attr("id", `area-clip${this.props.box.key}`) + .append("svg:rect") + .attr("x", 0) + .attr("y", 0) + .attr("width", this.graph.width) + .attr("height", this.graph.height); + + this.graph.svg = this.graph.svg.append("g").attr("class", "top-group") + .attr("transform", "translate(" + this.graph.margin.left + "," + this.graph.margin.top + ")"); + + this.graph.focus = this.graph.svg.append("g").attr("class", "tooltip-focus"); + + this.graph.area = this.graph.svg.append("g") + .attr("class", "stack-area") + .attr("clip-path",`url(#area-clip${this.props.box.key})`); + + this.graph.line = this.graph.svg.append("g") + .attr("class", "line-plot") + .attr("clip-path",`url(#area-clip${this.props.box.key})`); + + + + this.graph.overlay = this.graph.svg.append("g").append("rect").attr("class", "tooltip-overlay").attr("width", this.graph.width).attr("height", this.graph.height); + + this.graph.svg.on("mouseover", function () { + let layer = that.refs.lineChartRoot.parentNode.parentNode.parentNode.parentNode.parentNode; + layer.style.zIndex = 9; + + let hoverLine = that.graph.focus.select("line.x-hover-line"); + if (hoverLine.size() > 0) { + hoverLine.style("display", "block"); + } + + let instanceMetricCount = {}; + for (let counterKey in that.state.counters) { + let thisOption = that.props.box.option.filter((d) => {return d.counterKey === counterKey})[0]; + if (!thisOption) { + break; + } + + for (let i = 0; i < that.props.objects.length; i++) { + const obj = that.props.objects[i]; + if (thisOption.familyName === obj.objFamily) { + if (!instanceMetricCount[obj.objHash]) { + instanceMetricCount[obj.objHash] = 0; + } + let color; + if (that.props.config.graph.color === "metric") { + color = InstanceColor.getMetricColor(thisOption.counterKey, that.props.config.colorType); + } else { + color = InstanceColor.getInstanceColors(that.props.config.colorType)[obj.objHash][(instanceMetricCount[obj.objHash]++) % 5]; + } + that.mouseOverObject(that.props.objects[i], thisOption, color); + } + } + } + + that.graph.focus.selectAll("circle").style("display", "block"); + + //that.props.showTooltip(); + }); + this.graph.svg.on("mouseout",() =>{ + + let layer = this.refs.lineChartRoot.parentNode.parentNode.parentNode.parentNode.parentNode; + layer.style.zIndex = 5; + this.graph.focus.selectAll("circle").style("display", "none"); + this.graph + .focus + .select("line.x-hover-line") + .style("display", "none"); + + this.props.hideTooltip(); + if(!this.props.timeFocus.keep) + this.props.setTimeFocus(false, null, that.props.box.key); + + + }); + + this.graph.bisector = d3.bisector(function (d) { + return d.time; + }).left; + + this.graph.svg.on("contextmenu",()=>{ + // console.log(d3.event.which); + d3.event.preventDefault(); + // e.preventDefault(); + if(!this.props.timeFocus.keep){ + //toggle + //tooltip hidel + that.graph.focus.select("line.x-hover-line").style("display","none"); + that.graph.focus.selectAll("circle").style("display","none"); + that.props.hideTooltip(); + } + this.props.setTimeFocus( + this.props.timeFocus.active, + this.props.timeFocus.time, + this.props.timeFocus.id, + !this.props.timeFocus.keep + ); + }); + + this.graph.svg.on("mousemove", function () { + + + + let tooltip = {}; + tooltip.lines = []; + + let xPos = d3.mouse(this)[0]; + let yPos = d3.mouse(this)[1]; + if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) { + let box = that.refs.lineChart.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode; + if (window.getComputedStyle) { + let style = getComputedStyle(box); + let transform = style.transform || style.webkitTransform || style.mozTransform; + let mat = transform.match(/^matrix3d\((.+)\)$/); + if (mat) return parseFloat(mat[1].split(', ')[13]); + mat = transform.match(/^matrix\((.+)\)$/); + let transformX = mat ? parseFloat(mat[1].split(', ')[4]) : 0; + let transformY = mat ? parseFloat(mat[1].split(', ')[5]) : 0; + xPos = xPos - transformX; + yPos = yPos - transformY; + } + } + + let x0 = that.graph.x.invert(xPos); + let timeFormat = d3.timeFormat(that.graph.fullTimeFormat); + + let instanceMetricCount = {}; + + for (let counterKey in that.state.counters) { + let thisOption = that.props.box.option.filter((d) => {return d.counterKey === counterKey})[0]; + let dataIndex = that.graph.bisector(that.state.counters[counterKey], x0, 0); + + if (!that.state.counters[counterKey][dataIndex]) { + break; + } + + if (tooltip.timeValue && (tooltip.timeValue < that.state.counters[counterKey][dataIndex].time)) { + + } else { + tooltip.time = timeFormat(that.state.counters[counterKey][dataIndex].time); + tooltip.timeValue = that.state.counters[counterKey][dataIndex].time; + } + + if (!thisOption) { + break; + } + that.counterSum = 0; + for (let i = 0; i < that.props.objects.length; i++) { + const obj = that.props.objects[i]; + if (thisOption.familyName === obj.objFamily) { + + if (!instanceMetricCount[obj.objHash]) { + instanceMetricCount[obj.objHash] = 0; + } + let color; + if (that.props.config.graph.color === "metric") { + color = InstanceColor.getMetricColor(thisOption.counterKey, that.props.config.colorType); + } else { + color = InstanceColor.getInstanceColors(that.props.config.colorType)[obj.objHash][(instanceMetricCount[obj.objHash]++) % 5]; + } + that.mouseMoveObject(that.props.objects[i], thisOption, counterKey, dataIndex, color, tooltip); + } + } + } + + let hoverLine = that.graph.focus.select("line.x-hover-line").style('display','block'); + if (hoverLine.size() < 1) { + hoverLine = that.graph.focus.append("line").attr("class", "x-hover-line hover-line").attr("y1", 0).attr("y2", that.graph.height); + } + + let xPosition = that.graph.x(tooltip.timeValue); + + if (tooltip.timeValue) { + hoverLine.attr("x1", xPosition); + hoverLine.attr("x2", xPosition); + } + + if (tooltip && tooltip.lines) { + for (let i = 0; i < tooltip.lines.length; i++) { + + if (!isNaN(tooltip.lines[i].value)) { + let circle = that.graph.focus.select("circle." + tooltip.lines[i].circleKey).style('display','block'); + if (circle.size() > 0) { + circle.attr("cx", xPosition); + circle.attr("cy", that.graph.y(tooltip.lines[i].value)); + } + } + } + } + + tooltip.chartType = that.chartType; + tooltip.counterSum = numeral(that.counterSum).format(that.props.config.numberFormat); + that.graph.currentTooltipTime = tooltip.timeValue; + + if(!that.props.timeFocus.keep){ + that.props.setTimeFocus(true,x0.getTime(),that.props.box.key); + } + that.props.showTooltip(xPos, yPos, that.graph.margin.left, that.graph.margin.top, tooltip); + + }); + + + //-- brush + // Add the brush feature using the d3.brush function + this.brush = d3.brushX() + .extent([[0, 0], [this.graph.width, this.graph.height]]) + .on("end", this.zoomBrush); + this.graph.svg.append("g").attr("class", "brush").call(this.brush); + + this.graphAxis(this.graph.width, this.graph.height, true); + + }; + + axisUp = () => { + this.graph.maxY = this.graph.maxY * 1.2; + this.graphAxis(this.graph.width, this.graph.height, false); + this.draw(); + }; + + axisDown = () => { + + this.graph.maxY = this.graph.maxY * 0.8; + if (this.graph.maxY < this.graph.autoMaxY) { + this.graph.maxY = this.graph.autoMaxY; + } + + this.graphAxis(this.graph.width, this.graph.height, false); + this.draw(); + }; + + stopProgation = (e) => { + e.stopPropagation(); + }; + + render() { + return ( +
+
+
+
+ {this.state.noData &&
+
+
NO DATA RECEIVED
+
+
} +
+ ); + } +} + +let mapStateToProps = (state) => { + return { + objects: state.target.objects, + config: state.config, + filterMap: state.target.filterMap, + timeFocus: state.timeFocus, + range: state.range, + }; +}; + +let mapDispatchToProps = (dispatch) => { + return { + setTimeFocus: (active, time, boxKey,keep) => dispatch(setTimeFocus(active, time, boxKey,keep)), + setRealTimeValue: (realTime, longTerm, value) => dispatch(setRealTimeValue(realTime, longTerm, value)), + setRangeDateHoursMinutes: (date, hours, minutes) => dispatch(setRangeDateHoursMinutes(date, hours, minutes)), + setRealTimeRangeStepValue: (realTime, longTerm, value, range, step) => dispatch(setRealTimeRangeStepValue(realTime, longTerm, value, range, step)), + setSearchCondition: (from, to, time) => dispatch(setSearchCondition(from, to, time)), + }; +}; + +const LineChart_BACKUP = connect(mapStateToProps, mapDispatchToProps)(LineChart); +export default withRouter(LineChart_BACKUP); diff --git a/src/components/Paper/LineChart/LineChart.js b/src/components/Paper/LineChart/LineChart.js index 32ba750..6aa4768 100644 --- a/src/components/Paper/LineChart/LineChart.js +++ b/src/components/Paper/LineChart/LineChart.js @@ -2,11 +2,8 @@ import React, {Component} from 'react'; import './LineChart.css'; import {connect} from 'react-redux'; import {withRouter} from 'react-router-dom'; -import * as d3 from "d3"; -import * as _ from "lodash"; import ServerDate from "../../../common/ServerDate"; -import InstanceColor from "../../../common/InstanceColor"; -import numeral from "numeral"; +import Line from "./Line"; import { setRangeDateHoursMinutes, setRealTimeRangeStepValue, @@ -14,7 +11,6 @@ import { setSearchCondition, setTimeFocus } from "../../../actions"; -import moment from 'moment'; class LineChart extends Component { @@ -52,21 +48,30 @@ class LineChart extends Component { this.state = { counters: {}, maxY: null, - autoMaxY: null + autoMaxY: null, + options: null } + } + resize(){ + let box = this.refs.lineChart.parentNode.parentNode.parentNode.parentNode; + this.graph.width = box.offsetWidth - this.graph.margin.left - this.graph.margin.right; + this.graph.height = box.offsetHeight - this.graph.margin.top - this.graph.margin.bottom - 27; + } componentDidMount() { if (this.props.config.colorType === "white") { this.graph.opacity = 0.3; } else { this.graph.opacity = 0.6; } - - this.graph.timeFormat = this.props.config.minuteFormat; this.graph.fullTimeFormat = this.props.config.dateFormat + " " + this.props.config.timeFormat; - this.graphInit(); + this.resize(); + + this.setState({ + options : {...this.graph, type : this.chartType} + }); } shouldComponentUpdate(nextProps, nextState) { @@ -200,7 +205,7 @@ class LineChart extends Component { //- 이전 툴팁이 고정 되었으면 자동 해지 할수 있도록 이벤트 체크 this.chartType = nextProps.box.values['chartType']; if(!this.props.timeFocus.keep) { - this.drawTimeFocus(); + // this.drawTimeFocus(); } } @@ -322,897 +327,12 @@ class LineChart extends Component { }; - removeObjLine(prevList, currentList) { - // 제외된 인스턴스 찾기 - let currentInstanceMap = {}; - if (currentList && currentList.length > 0) { - for (let i = 0; i < currentList.length; i++) { - currentInstanceMap[currentList[i].objHash] = true; - } - } - - if (prevList && prevList.length > 0) { - for (let i = 0; i < prevList.length; i++) { - if (!currentInstanceMap[prevList[i].objHash]) { - - for (let counterKey in this.state.counters) { - - let thisOption = null; - for (let j = 0; j < this.props.box.option.length; j++) { - if (this.props.box.option[j].counterKey === counterKey) { - thisOption = this.props.box.option[j]; - break; - } - } - - if (thisOption) { - this.removeObjectLineOnly(prevList[i], counterKey); - } - } - } - } - } - } - - componentDidUpdate = (prevProps, prevState) => { - if (prevProps.layoutChangeTime !== this.props.layoutChangeTime) { - if (this.graphResize()) { - this.draw(); - } - } else { - this.graphResize(); - this.draw(); - this.removeObjLine(prevProps.objects, this.props.objects); - } - }; - - - - moveTooltip = () => { - if (this.graph.currentTooltipTime) { - let xPosition = this.graph.x(this.graph.currentTooltipTime); - this.graph.focus.selectAll("circle").attr("cx", xPosition); - - let hoverLine = this.graph.focus.select("line.x-hover-line"); - hoverLine.attr("x1", xPosition); - hoverLine.attr("x2", xPosition); - } - }; - - leftAxisFormat = (d) => { - return numeral(d).format('0.0a'); - }; - setAnimation(svg){ - const {realTime} = this.props.range; - return realTime ? svg : svg.transition().duration(500); - } - getTimeFormat(){ - if( this.state.endTime-this.state.startTime < (60000 * 5)){ - return this.graph.timeFormatSec; - } else{ - return this.graph.timeFormat; - } - } - graphAxis = (width, height, init) => { - this.graph.x = d3.scaleTime().range([0, width]); - this.graph.y = d3.scaleLinear().range([height, 0]); - - this.graph.x.domain([this.state.startTime, this.state.endTime]); - this.graph.y.domain([0, this.graph.maxY]); - - let xAxisCount = Math.floor(this.graph.width / this.graph.xAxisWidth); - if (xAxisCount < 1) { - xAxisCount = 1; - } - - let yAxisCount = Math.floor(this.graph.height / this.graph.yAxisHeight); - if (yAxisCount < 1) { - yAxisCount = 1; - } - - //- y - if (init) { - this.graph.svg.insert("g", ":first-child").attr("class", "axis-y").call(d3.axisLeft(this.graph.y).tickFormat(this.leftAxisFormat).ticks(yAxisCount)); - this.graph.svg.insert("g", ":first-child").attr("class", "grid-y").style("stroke-dasharray", "5 2").style("opacity", this.graph.opacity).call(d3.axisLeft(this.graph.y).tickSize(-this.graph.width).tickFormat("").ticks(yAxisCount)); - } else { - this.setAnimation(this.graph.svg.select(".axis-x")) - .call(d3.axisBottom(this.graph.x).tickFormat(d3.timeFormat(this.getTimeFormat())).ticks(xAxisCount)); - this.setAnimation(this.graph.svg.select(".grid-x")) - .call(d3.axisBottom(this.graph.x).tickSize(-this.graph.height).tickFormat("").ticks(xAxisCount)); - } - - if (init) { - this.graph.svg.insert("g", ":first-child").attr("class", "axis-x").attr("transform", "translate(0," + this.graph.height + ")").call(d3.axisBottom(this.graph.x).tickFormat(d3.timeFormat(this.graph.timeFormat)).ticks(xAxisCount)); - this.graph.svg.insert("g", ":first-child").attr("class", "grid-x").style("stroke-dasharray", "5 2").style("opacity", this.graph.opacity).attr("transform", "translate(0," + this.graph.height + ")").call(d3.axisBottom(this.graph.x).tickSize(-this.graph.height).tickFormat("").ticks(xAxisCount)); - - } else { - this.graph.svg.select(".axis-y").transition().duration(500).call(d3.axisLeft(this.graph.y).tickFormat(this.leftAxisFormat).ticks(yAxisCount)); - this.graph.svg.select(".grid-y").transition().duration(500).call(d3.axisLeft(this.graph.y).tickSize(-this.graph.width).tickFormat("").ticks(yAxisCount)); - } - - - }; - - manualTooltipHide = ( ) =>{ - - let layer = this.refs.lineChartRoot.parentNode.parentNode.parentNode.parentNode.parentNode; - layer.style.zIndex = 5; - this.graph.focus.selectAll("circle").style("display", "none"); - - let hoverLine = this.graph.focus.select("line.x-hover-line"); - if (hoverLine.size() > 0) { - hoverLine.style("display", "none"); - } - this.props.hideTooltip(); - }; - - - replaceAll(str, searchStr, replaceStr) { - return str.split(searchStr).join(replaceStr); - } - - replaceName (name) { - if (name) { - return this.replaceAll(this.replaceAll(name, "%", "_PCT_"), '$', '_DOLLAR_'); - } - return name; - } - removeStackFill = () =>{ - this.graph.svg.select('g.stack-area').selectAll('path.line') - .transition() - .delay(100) - .remove(); - }; - - removeLineFill = () =>{ - for(const obj of this.props.objects) { - for (let counterKey in this.state.counters) { - let areaClass = "area-" + obj.objHash + "-" + this.replaceName(counterKey); - this.graph.line.selectAll("path." + areaClass) - .transition() - .delay(100) - .remove(); - } - } - }; - removeLine = () =>{ - for(const obj of this.props.objects) { - for (let counterKey in this.state.counters) { - let lineClass = "line-" + obj.objHash + "-" + this.replaceName(counterKey); - this.graph.line.selectAll("path." + lineClass) - .transition() - .delay(100) - .remove(); - } - } - }; - - - removeObjectLine = (obj, counterKey) => { - let pathClass = "line-" + obj.objHash + "-" + this.replaceName(counterKey); - let path = this.graph.line.selectAll("path." + pathClass); - - // 데이터 삭제 - let counters = Object.assign({}, this.state.counters); - if (counters[counterKey]) { - delete counters[counterKey]; - } - delete this.state.counters[counterKey]; - this.setState({ - counters: counters - }); - - // 라인 그래프 삭제 - if (path && path.size() > 0) { - path.remove(); - } - - // 툴팁 그래프 삭제 - let circleKey = "circle-" + obj.objHash + "_" + this.replaceName(counterKey); - let circle = this.graph.focus.selectAll("circle." + circleKey); - - if (circle.size() > 0) { - circle.remove(); - } - - // 제목 삭제 - this.props.removeTitle(counterKey); - }; - - removeObjectLineOnly = (obj, counterKey) => { - let pathClass = "line-" + obj.objHash + "-" + this.replaceName(counterKey); - let path = this.graph.line.selectAll("path." + pathClass); - - // 라인 그래프 삭제 - if (path && path.size() > 0) { - path.remove(); - } - - // 툴팁 그래프 삭제 - let circleKey = "circle-" + obj.objHash + "_" + this.replaceName(counterKey); - let circle = this.graph.focus.selectAll("circle." + circleKey); - - if (circle.size() > 0) { - circle.remove(); - } - - }; - - drawStackArea=(thisOption,counterKey) => { - - let instanceMetricCount = {}; - const color = {}; - //- instance color making - for (const attr in this.props.objects) { - const _obj = this.props.objects[attr]; - if (_obj.objFamily === thisOption.familyName) { - if (!instanceMetricCount[_obj.objHash]) { - instanceMetricCount[_obj.objHash] = 0; - } - if (this.props.config.graph.color === "metric") { - const _cl = InstanceColor.getMetricColor(thisOption.counterKey, this.props.config.colorType); - color[_obj.objHash] = _cl; - } else { - const _cl = InstanceColor.getInstanceColors(this.props.config.colorType)[_obj.objHash][(instanceMetricCount[_obj.objHash]++) % 5]; - color[_obj.objHash] = _cl; - } - } - } - //- instance data flat data making - const stackData = _(this.state.counters[counterKey]) - .map((d) => { - const _r = Object.keys(d.data).map(key => { - const _keyValue = []; - _keyValue['objHash'] = d.data[key].objHash; - _keyValue['time'] = d.time; - _keyValue['value'] = Number(d.data[key].value); - _keyValue['color'] = color[d.data[key].objHash]; - return _keyValue; - }); - return _r; - }).flatMapDepth().value(); - //- 인스턴스 별 데이터로 변환 - let ld = d3.nest().key(d => d.objHash).entries(stackData); - const _sort = []; - - //- 인스턴스 순서 정렬 - for (const attr in this.props.objects) { - const _obj = this.props.objects[attr]; - const _find = _.findIndex(ld, (o) => o.key === _obj.objHash); - if(_find > -1 ){ - _sort.push(ld[_find]); - } - - } - //- 인스턴스 그리기 - const area = d3.area().curve(d3[this.props.config.graph.curve]) - .x(d =>{ - return this.graph.x(d[0]); - }) - .y0(d => this.graph.y(d[2])) - .y1(d => this.graph.y(d[1])); - - if (this.props.config.graph.break === "Y") { - area.defined((d)=>{ - return !isNaN(d[0]) && !isNaN(d[1]) && !isNaN(d[2]); - }) - } - - //- 시간 별 Y축 데이터 어그리게이션 - let pre = {}; - //- 차트 갱신 - let paintGroup = this.graph.area.selectAll("path.line") - .data(_sort) - .attr("d",(d)=> { - const _d = _.map(d.values,(_node) =>{ - const _key = _node.time; - const pre_v = pre[_key] ? pre[_key] : 0; - const next_v = pre_v + Number(_node.value); - pre[_key] = next_v; - return [_node.time,next_v,pre_v]; - }); - return area(_d); - }); - - //- 차트 생성 - paintGroup.enter() - .append('path') - .attr("d",(d)=> { - const _d = _.map(d.values,(_node) =>{ - const _key = _node.time; - const pre_v = pre[_key] ? pre[_key] : 0; - const next_v = pre_v + _node.value; - pre[_key] = next_v; - return [_node.time,next_v,pre_v]; - }); - return area(_d); - }) - .attr('class',(d)=> `line ${d.key}` ) - .attr('data-col-name', (d)=> d.key) - .style("fill", (d)=> { - return color[d.key]; - }) - .attr("fill-opacity", this.props.config.graph.fillOpacity) - .attr("stroke",(d) =>{ - return color[d.key]; - }) - .style("stroke-width", this.props.config.graph.width) - .style("opacity", this.props.config.graph.opacity); - - //- 차트 갱신 후 데이터 삭제 - paintGroup.exit().remove(); - }; - - - drawObjectLine = (obj, option, counterKey, color) => { - const that = this; - if (this.props.box.values['chartType'] === "LINE FILL") { - - let areaClass = "area-" + obj.objHash + "-" + this.replaceName(counterKey); - let area = this.graph.line.selectAll("path." + areaClass) - - - if (area.size() < 1) { - area = this.graph.line.insert("path", ":first-child").attr("class", areaClass).style("stroke", color); - } - - let valueArea = d3.area().curve(d3[this.props.config.graph.curve]) - .x(function (d) { - return that.graph.x(d.time); - }) - .y0(that.graph.height) - .y1(function (counter) { - let objData = counter.data[obj.objHash]; - if (objData) { - return that.graph.y(objData.value); - } else { - return that.graph.y(0); - } - }); - - - if (this.props.config.graph.break === "Y") { - valueArea.defined(function (d) { - let objData = d.data ? d.data[obj.objHash] : null; - return objData && !isNaN(d.time) && !isNaN(objData.value) && !isNaN(that.graph.y(objData.value)); - }) - } - - - if (!this.props.filterMap[obj.objHash]) { - area.data([that.state.counters[counterKey]]) - .attr("d", valueArea) - .style("fill", color) - .style("opacity", 0) - .transition() - .delay(100); - } else { - area.data([that.state.counters[counterKey]]) - .attr("d", valueArea) - .style("fill", color) - .style("opacity", this.props.config.graph.fillOpacity) - .transition() - .delay(100); - } - } - - - - let pathClass = "line-" + obj.objHash + "-" + this.replaceName(counterKey); - let path = this.graph.line.selectAll("path." + pathClass); - - if (path.size() < 1) { - path = this.graph.line.insert("path", ":first-child").attr("class", pathClass).style("stroke", color); - if (this.props.config.graph.color === "instance") { - if (this.props.config.colorType === "white") { - this.props.setTitle(counterKey, option.title, "#333", option.familyName); - } else { - this.props.setTitle(counterKey, option.title, "white", option.familyName); - } - } else { - this.props.setTitle(counterKey, option.title, color, option.familyName); - } - } else { - path.style("stroke", color); - if (this.props.config.graph.color === "instance") { - if (this.props.config.colorType === "white") { - this.props.setTitle(counterKey, option.title, "#333", option.familyName); - } else { - this.props.setTitle(counterKey, option.title, "white", option.familyName); - } - } else { - this.props.setTitle(counterKey, option.title, color, option.familyName); - } - - let circleKey = "circle-" + obj.objHash + "_" + this.replaceName(counterKey); - that.graph.focus.select("circle." + circleKey).attr("stroke", color); - } - - let valueLine = d3.line().curve(d3[this.props.config.graph.curve]); - - if (this.props.config.graph.break === "Y") { - valueLine.defined(function (d) { - let objData = d.data ? d.data[obj.objHash] : null; - return objData && !isNaN(d.time) && !isNaN(objData.value) && !isNaN(that.graph.y(objData.value)); - }) - } - - valueLine.x(function (d) { - return that.graph.x(d.time); - }).y(function (counter) { - let objData = counter.data[obj.objHash]; - if (objData) { - return that.graph.y(objData.value); - } else { - return that.graph.y(0); - } - }); - - if (!this.props.filterMap[obj.objHash]) { - this.setAnimation(path.data([that.state.counters[counterKey]])).attr("d", valueLine).style("stroke-width", this.props.config.graph.width).style("opacity", 0); - } else { - this.setAnimation(path.data([that.state.counters[counterKey]])).attr("d", valueLine).style("stroke-width", this.props.config.graph.width).style("opacity", this.props.config.graph.opacity); - } - - }; - - drawTimer = null; - - draw = () => { - - let that = this; - if (this.drawTimer) { - clearTimeout(this.drawTimer); - this.drawTimer = null; - } - - this.drawTimer = setTimeout(() => { - if (this.refs.lineChart && this.graph.svg) { - - this.graphAxis(this.graph.width, this.graph.height, false); - if (this.props.objects) { - let instanceMetricCount = {}; - for (let counterKey in this.state.counters) { - let thisOption = that.props.box.option.filter((d) => {return d.counterKey === counterKey})[0]; - if (!thisOption) { - for (let i = 0; i < this.props.objects.length; i++) { - this.removeObjectLine(this.props.objects[i], counterKey); - } - } else if(this.chartType ==='STACK AREA') { - this.drawStackArea(thisOption,counterKey); - } else { - for (let i = 0; i < this.props.objects.length; i++) { - const obj = that.props.objects[i]; - - if (obj.objFamily === thisOption.familyName) { - if (!instanceMetricCount[obj.objHash]) { - instanceMetricCount[obj.objHash] = 0; - } - let color; - if (this.props.config.graph.color === "metric") { - color = InstanceColor.getMetricColor(thisOption.counterKey, this.props.config.colorType); - } else { - color = InstanceColor.getInstanceColors(this.props.config.colorType)[obj.objHash][(instanceMetricCount[obj.objHash]++) % 5]; - } - this.drawObjectLine(obj, thisOption, counterKey, color); - } - } - - } - } - } - } - - this.moveTooltip(); - if(this.props.timeFocus.keep) { - this.drawTimeFocus(true); - - } - }, 200); - - }; - - - drawTimeFocus=(isFixed=false)=>{ - - if( isFixed && !this.state.noData){ - if( Object.keys(this.state.counters).map(k => this.state.counters[k][0] ? this.state.counters[k][0].time : null).filter( t => this.props.timeFocus.time > t ).length ) { - let hoverLine = this.graph.focus.selectAll("line.focus-line"); - hoverLine.attr("x1", (d) => this.graph.x(d)) - .attr("x2", (d) => this.graph.x(d)); - - hoverLine.data([this.props.timeFocus.time]) - .enter() - .append("line") - .attr("class", "focus-line focus-hover-line") - .attr("y1", 0) - .attr("y2", this.graph.height) - .attr("x1", (d) => { - return this.graph.x(d); - }) - .attr("x2", (d) => this.graph.x(d)) - .exit() - .remove(); - }else{ - // 해제 - this.props.setTimeFocus(false,null,null,false); - } - - }else if( !this.state.noData - && this.props.timeFocus.active - && this.props.timeFocus.id !== this.props.box.key) { - let hoverLine = this.graph.focus.selectAll("line.focus-line"); - hoverLine.attr("x1", (d) =>this.graph.x(d)) - .attr("x2", (d) =>this.graph.x(d)); - - hoverLine.data([this.props.timeFocus.time]) - .enter() - .append("line") - .attr("class", "focus-line focus-hover-line") - .attr("y1", 0) - .attr("y2", this.graph.height) - .attr("x1", (d) =>{ - return this.graph.x(d); - }) - .attr("x2", (d) =>this.graph.x(d)) - .exit() - .remove(); - }else{ - this.graph.focus.select("line.focus-line").remove(); - } - }; - zoomBrush = () =>{ - const extent=d3.event.selection; - const {realTime} = this.props.range; - if(extent) { - this.graph.svg.select(".brush").call(this.brush.move, null); - const endTime = this.graph.x.invert(extent[1]); - const startTime = this.graph.x.invert(extent[0]); - if(realTime) { - //- 조회 - const startDate= moment(startTime); - this.props.setRealTimeRangeStepValue(false, this.props.range.longTerm, - this.props.range.value, - this.props.config.range.shortHistoryRange, this.props.config.range.shortHistoryStep); - this.props.setRangeDateHoursMinutes(startDate, startDate.hours(), startDate.minutes()); - this.props.setSearchCondition(startTime.valueOf(), endTime.valueOf(), new Date().getTime()); - - }else{ - //- 확대 - this.setState({ - startTime : startTime.getTime(), - endTime : endTime.getTime() - }); - } - }else{ - const {time,countersHistoryTo,countersHistoryFrom} = this.props; - if(realTime){ - this.setState({ - startTime : time - (1000*60*10), - endTime : time - }); - }else{ - this.setState({ - startTime : countersHistoryFrom, - endTime : countersHistoryTo - }); - } - - } - - }; - - - graphResize = () => { - let resized = false; - let box = this.refs.lineChart.parentNode.parentNode.parentNode.parentNode; - if ((box.offsetWidth - this.graph.margin.left - this.graph.margin.right !== this.graph.width) || (this.graph.height !== box.offsetHeight - this.graph.margin.top - this.graph.margin.bottom - 27)) { - resized = true; - this.graphInit(); - this.manualTooltipHide(); - } - - return resized; - }; - - mouseOverObject = (obj, thisOption, color) => { - let that = this; - let r = 3; - - let circleKey = "circle-" + obj.objHash + "_" + this.replaceName(thisOption.counterKey); - let circle = that.graph.focus.select("circle." + circleKey); - if (circle.size() < 1) { - circle = that.graph.focus.append("circle").attr("class", circleKey).attr("r", r).attr("stroke", color); - } - - if (this.props.filterMap[obj.objHash]) { - circle.style("opacity", 1); - } else { - circle.style("opacity", 0); - } - - }; - - mouseMoveObject = (obj, thisOption, counterKey, dataIndex, color, tooltip) => { - let that = this; - - let circleKey = "circle-" + obj.objHash + "_" + this.replaceName(thisOption.counterKey); - let unit = that.state.counters[counterKey][dataIndex].data[obj.objHash] ? that.state.counters[counterKey][dataIndex].data[obj.objHash].unit : ""; - - let valueOutput = obj.objHash && that.state.counters[counterKey][dataIndex].data[obj.objHash] ? that.state.counters[counterKey][dataIndex].data[obj.objHash].value : null ; - const valueOrigin = obj.objHash && that.state.counters[counterKey][dataIndex].data[obj.objHash] ? that.state.counters[counterKey][dataIndex].data[obj.objHash].value : null ; - if( this.chartType === "STACK AREA" && valueOutput ){ - valueOutput = this.counterSum + valueOutput; - this.counterSum = valueOutput; - } - - if (that.state.counters[counterKey][dataIndex].time) { - if (this.props.filterMap[obj.objHash]) { - tooltip.lines.push({ - instanceName: obj.objName, - circleKey: circleKey, - metricName: thisOption.title, - value: valueOutput ? valueOutput : null, - displayValue: valueOrigin ? numeral(valueOrigin).format(this.props.config.numberFormat) + " " + unit : null, - color: color - }); - } - } else { - that.graph.focus.select("circle." + circleKey).style("display", "none"); - } - - return true; - }; - - graphInit = () => { - - let that = this; - let box = this.refs.lineChart.parentNode.parentNode.parentNode.parentNode; - this.graph.width = box.offsetWidth - this.graph.margin.left - this.graph.margin.right; - this.graph.height = box.offsetHeight - this.graph.margin.top - this.graph.margin.bottom - 27; - - let svg = d3.select(this.refs.lineChart).select("svg"); - if (svg.size() > 0) { - svg.remove(); - } - - this.graph.svg = d3.select(this.refs.lineChart) - .append("svg") - .attr("width", this.graph.width + this.graph.margin.left + this.graph.margin.right) - .attr("height", this.graph.height + this.graph.margin.top + this.graph.margin.bottom); - - d3.select(this.refs.lineChart).select("svg").append("defs") - .append("svg:clipPath") - .attr("id", `area-clip${this.props.box.key}`) - .append("svg:rect") - .attr("x", 0) - .attr("y", 0) - .attr("width", this.graph.width) - .attr("height", this.graph.height); - - this.graph.svg = this.graph.svg.append("g").attr("class", "top-group") - .attr("transform", "translate(" + this.graph.margin.left + "," + this.graph.margin.top + ")"); - - this.graph.focus = this.graph.svg.append("g").attr("class", "tooltip-focus"); - - this.graph.area = this.graph.svg.append("g") - .attr("class", "stack-area") - .attr("clip-path",`url(#area-clip${this.props.box.key})`); - - this.graph.line = this.graph.svg.append("g") - .attr("class", "line-plot") - .attr("clip-path",`url(#area-clip${this.props.box.key})`); - - - - this.graph.overlay = this.graph.svg.append("g").append("rect").attr("class", "tooltip-overlay").attr("width", this.graph.width).attr("height", this.graph.height); - - this.graph.svg.on("mouseover", function () { - let layer = that.refs.lineChartRoot.parentNode.parentNode.parentNode.parentNode.parentNode; - layer.style.zIndex = 9; - - let hoverLine = that.graph.focus.select("line.x-hover-line"); - if (hoverLine.size() > 0) { - hoverLine.style("display", "block"); - } - - let instanceMetricCount = {}; - for (let counterKey in that.state.counters) { - let thisOption = that.props.box.option.filter((d) => {return d.counterKey === counterKey})[0]; - if (!thisOption) { - break; - } - - for (let i = 0; i < that.props.objects.length; i++) { - const obj = that.props.objects[i]; - if (thisOption.familyName === obj.objFamily) { - if (!instanceMetricCount[obj.objHash]) { - instanceMetricCount[obj.objHash] = 0; - } - let color; - if (that.props.config.graph.color === "metric") { - color = InstanceColor.getMetricColor(thisOption.counterKey, that.props.config.colorType); - } else { - color = InstanceColor.getInstanceColors(that.props.config.colorType)[obj.objHash][(instanceMetricCount[obj.objHash]++) % 5]; - } - that.mouseOverObject(that.props.objects[i], thisOption, color); - } - } - } - - that.graph.focus.selectAll("circle").style("display", "block"); - - //that.props.showTooltip(); - }); - this.graph.svg.on("mouseout",() =>{ - - let layer = this.refs.lineChartRoot.parentNode.parentNode.parentNode.parentNode.parentNode; - layer.style.zIndex = 5; - this.graph.focus.selectAll("circle").style("display", "none"); - this.graph - .focus - .select("line.x-hover-line") - .style("display", "none"); - - this.props.hideTooltip(); - if(!this.props.timeFocus.keep) - this.props.setTimeFocus(false, null, that.props.box.key); - - - }); - - this.graph.bisector = d3.bisector(function (d) { - return d.time; - }).left; - - this.graph.svg.on("contextmenu",()=>{ - // console.log(d3.event.which); - d3.event.preventDefault(); - // e.preventDefault(); - if(!this.props.timeFocus.keep){ - //toggle - //tooltip hidel - that.graph.focus.select("line.x-hover-line").style("display","none"); - that.graph.focus.selectAll("circle").style("display","none"); - that.props.hideTooltip(); - } - this.props.setTimeFocus( - this.props.timeFocus.active, - this.props.timeFocus.time, - this.props.timeFocus.id, - !this.props.timeFocus.keep - ); - }); - - this.graph.svg.on("mousemove", function () { - - - - let tooltip = {}; - tooltip.lines = []; - - let xPos = d3.mouse(this)[0]; - let yPos = d3.mouse(this)[1]; - if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) { - let box = that.refs.lineChart.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode; - if (window.getComputedStyle) { - let style = getComputedStyle(box); - let transform = style.transform || style.webkitTransform || style.mozTransform; - let mat = transform.match(/^matrix3d\((.+)\)$/); - if (mat) return parseFloat(mat[1].split(', ')[13]); - mat = transform.match(/^matrix\((.+)\)$/); - let transformX = mat ? parseFloat(mat[1].split(', ')[4]) : 0; - let transformY = mat ? parseFloat(mat[1].split(', ')[5]) : 0; - xPos = xPos - transformX; - yPos = yPos - transformY; - } - } - - let x0 = that.graph.x.invert(xPos); - let timeFormat = d3.timeFormat(that.graph.fullTimeFormat); - - let instanceMetricCount = {}; - - for (let counterKey in that.state.counters) { - let thisOption = that.props.box.option.filter((d) => {return d.counterKey === counterKey})[0]; - let dataIndex = that.graph.bisector(that.state.counters[counterKey], x0, 0); - - if (!that.state.counters[counterKey][dataIndex]) { - break; - } - - if (tooltip.timeValue && (tooltip.timeValue < that.state.counters[counterKey][dataIndex].time)) { - - } else { - tooltip.time = timeFormat(that.state.counters[counterKey][dataIndex].time); - tooltip.timeValue = that.state.counters[counterKey][dataIndex].time; - } - - if (!thisOption) { - break; - } - that.counterSum = 0; - for (let i = 0; i < that.props.objects.length; i++) { - const obj = that.props.objects[i]; - if (thisOption.familyName === obj.objFamily) { - - if (!instanceMetricCount[obj.objHash]) { - instanceMetricCount[obj.objHash] = 0; - } - let color; - if (that.props.config.graph.color === "metric") { - color = InstanceColor.getMetricColor(thisOption.counterKey, that.props.config.colorType); - } else { - color = InstanceColor.getInstanceColors(that.props.config.colorType)[obj.objHash][(instanceMetricCount[obj.objHash]++) % 5]; - } - that.mouseMoveObject(that.props.objects[i], thisOption, counterKey, dataIndex, color, tooltip); - } - } - } - - let hoverLine = that.graph.focus.select("line.x-hover-line").style('display','block'); - if (hoverLine.size() < 1) { - hoverLine = that.graph.focus.append("line").attr("class", "x-hover-line hover-line").attr("y1", 0).attr("y2", that.graph.height); - } - - let xPosition = that.graph.x(tooltip.timeValue); - - if (tooltip.timeValue) { - hoverLine.attr("x1", xPosition); - hoverLine.attr("x2", xPosition); - } - - if (tooltip && tooltip.lines) { - for (let i = 0; i < tooltip.lines.length; i++) { - - if (!isNaN(tooltip.lines[i].value)) { - let circle = that.graph.focus.select("circle." + tooltip.lines[i].circleKey).style('display','block'); - if (circle.size() > 0) { - circle.attr("cx", xPosition); - circle.attr("cy", that.graph.y(tooltip.lines[i].value)); - } - } - } - } - - tooltip.chartType = that.chartType; - tooltip.counterSum = numeral(that.counterSum).format(that.props.config.numberFormat); - that.graph.currentTooltipTime = tooltip.timeValue; - - if(!that.props.timeFocus.keep){ - that.props.setTimeFocus(true,x0.getTime(),that.props.box.key); - } - that.props.showTooltip(xPos, yPos, that.graph.margin.left, that.graph.margin.top, tooltip); - - }); - - - //-- brush - // Add the brush feature using the d3.brush function - this.brush = d3.brushX() - .extent([[0, 0], [this.graph.width, this.graph.height]]) - .on("end", this.zoomBrush); - this.graph.svg.append("g").attr("class", "brush").call(this.brush); - - this.graphAxis(this.graph.width, this.graph.height, true); - - }; - axisUp = () => { - this.graph.maxY = this.graph.maxY * 1.2; - this.graphAxis(this.graph.width, this.graph.height, false); - this.draw(); + }; axisDown = () => { - this.graph.maxY = this.graph.maxY * 0.8; - if (this.graph.maxY < this.graph.autoMaxY) { - this.graph.maxY = this.graph.autoMaxY; - } - - this.graphAxis(this.graph.width, this.graph.height, false); - this.draw(); }; stopProgation = (e) => { @@ -1222,14 +342,25 @@ class LineChart extends Component { render() { return (
-
+
+ + +
- {this.state.noData &&
-
-
NO DATA RECEIVED
-
-
} + { + this.state.noData &&
+
+
NO DATA RECEIVED
+
+
+ }
); } From e6b64e1c6befa9316cb4fce69524e4ae49f9d5c7 Mon Sep 17 00:00:00 2001 From: "yeosong.heo" Date: Thu, 24 Oct 2019 23:46:05 +0900 Subject: [PATCH 58/77] :construction: line chart refactoring --- src/components/Paper/LineChart/Line.js | 67 +++++++++++---------- src/components/Paper/LineChart/LineChart.js | 45 ++++++++------ 2 files changed, 61 insertions(+), 51 deletions(-) diff --git a/src/components/Paper/LineChart/Line.js b/src/components/Paper/LineChart/Line.js index 4672a51..ceef5ae 100644 --- a/src/components/Paper/LineChart/Line.js +++ b/src/components/Paper/LineChart/Line.js @@ -12,8 +12,8 @@ import * as d3 from "d3"; import numeral from "numeral"; class Line extends Component { - state ={ - svg : null, + state = { + g : null, }; constructor(props) { @@ -24,8 +24,10 @@ class Line extends Component { componentWillReceiveProps(nextProps){ // console.log("first read;;",nextProps.options,this.props.options); + // console.log("componentWillReceiveProps ====================>",this.state.g); if( nextProps.options !== this.props.options){ //- init + console.log('option changed.'); // this.prepare(null,nextProps.options); } }; @@ -35,34 +37,31 @@ class Line extends Component { const {realTime} = this.props.range; }; - prepare(svg,options){ - const {width,height,margin} = options; + prepare(g){ + const {width,height,margin} = this.props.options; + const {options} = this.props; // const {svg}= this.state; + // console.log('',svg,margin,width,height); - // if(!svg) return; - console.log(options); - this.svg = d3.select(svg) - .attr("width", width + margin.left + margin.right) - .attr("height", height + margin.top + margin.bottom); + this.svg = d3.select(g.parentNode); this.svg.append("defs") - .append("svg:clipPath") - .attr("id", `area-clip${this.props.box.key}`) - .append("svg:rect") - .attr("x", 0) - .attr("y", 0) - .attr("width", width) - .attr("height", height); - - this.top = this.svg.append("g") - .attr("class", "top-group") - .attr("transform", `translate(${margin.left},${margin.top}")`); - this.focus = this.top.append("g").attr("class", "tooltip-focus"); + .append("svg:clipPath") + .attr("id", `area-clip${this.props.box.key}`) + .append("svg:rect") + .attr("x", 0) + .attr("y", 0) + .attr("width", width) + .attr("height", height); - this.area = this.top.append("g") + this.top = d3.select(g).attr("transform", "translate(" + margin.left + "," + margin.top + ")"); + + this.focus = this.top.append("g").attr("class", "tooltip-focus"); + // + this.stackArea = this.top.append("g") .attr("class", "stack-area") .attr("clip-path",`url(#area-clip${this.props.box.key})`); - + // this.line = this.top.append("g") .attr("class", "line-plot") .attr("clip-path",`url(#area-clip${this.props.box.key})`); @@ -72,14 +71,14 @@ class Line extends Component { .on("end", this.zoomBrush); this.top.append("g").attr("class", "brush").call(this.brush); - - //Axis Draw + // + // //Axis Draw this.xAxis = d3.scaleTime().range([0, width]); this.yAxis = d3.scaleLinear().range([height, 0]); - + // this.xAxis.domain([this.props.startTime, this.props.endTime]); this.yAxis.domain([0, options.maxY]); - + // let xAxisCount = Math.floor(width / options.xAxisWidth); if (xAxisCount < 1) { xAxisCount = 1; @@ -101,13 +100,13 @@ class Line extends Component { .tickSize(-options.width) .tickFormat("").ticks(yAxisCount)); - this.top.insert("g", ":first-child") .attr("class", "axis-x") .attr("transform", "translate(0," + height + ")") .call(d3.axisBottom(this.xAxis) .tickFormat(d3.timeFormat(options.timeFormat)) .ticks(xAxisCount)); + this.top.insert("g", ":first-child") .attr("class", "grid-x") .style("stroke-dasharray", "5 2") @@ -119,21 +118,25 @@ class Line extends Component { .ticks(xAxisCount)); }; + componentDidMount() { + + } shouldComponentUpdate() { return false; }; onRef = (ref) => { - this.setState({ svg : d3.select(ref) } ,()=>{ - this.prepare(ref,this.props.options); + this.setState({ g : d3.select(ref) } ,()=>{ + this.prepare(ref); }); }; + render(){ return ( - - + + ); }; } diff --git a/src/components/Paper/LineChart/LineChart.js b/src/components/Paper/LineChart/LineChart.js index 6aa4768..b4ae38c 100644 --- a/src/components/Paper/LineChart/LineChart.js +++ b/src/components/Paper/LineChart/LineChart.js @@ -25,8 +25,8 @@ class LineChart extends Component { }, svg: null, overlay: null, - width: null, - height: null, + width: 700, + height: 50, x: null, y: null, path: null, @@ -49,17 +49,25 @@ class LineChart extends Component { counters: {}, maxY: null, autoMaxY: null, - options: null + options: {...this.graph}, } } resize(){ + console.log('resize....'); let box = this.refs.lineChart.parentNode.parentNode.parentNode.parentNode; this.graph.width = box.offsetWidth - this.graph.margin.left - this.graph.margin.right; this.graph.height = box.offsetHeight - this.graph.margin.top - this.graph.margin.bottom - 27; - + this.setState({ + options : {...this.graph, type : this.chartType} + }); } + + componentWillUnmount() { + window.removeEventListener("resize", this.resize); + }; componentDidMount() { + window.addEventListener("resize", this.resize); if (this.props.config.colorType === "white") { this.graph.opacity = 0.3; } else { @@ -69,9 +77,6 @@ class LineChart extends Component { this.graph.fullTimeFormat = this.props.config.dateFormat + " " + this.props.config.timeFormat; this.resize(); - this.setState({ - options : {...this.graph, type : this.chartType} - }); } shouldComponentUpdate(nextProps, nextState) { @@ -92,16 +97,16 @@ class LineChart extends Component { if( nextProps.box.values['chartType'] !== this.chartType){ //- LINE FILL => LINE Change if( nextProps.box.values['chartType'] === 'LINE' && this.chartType === 'LINE FILL' ){ - this.removeLineFill(); + // this.removeLineFill(); } //- LINE or LINE FILL => STACK; if( nextProps.box.values['chartType'] === 'STACK AREA' && ( this.chartType === 'LINE' || this.chartType === 'LINE FILL') ){ - this.removeLineFill(); - this.removeLine(); + // this.removeLineFill(); + // this.removeLine(); } // STACK => LINE or LINE FILL if( (nextProps.box.values['chartType'] === 'LINE' || nextProps.box.values['chartType'] === 'LINE FILL' ) && ( this.chartType === 'STACK AREA') ){ - this.removeStackFill(); + // this.removeStackFill(); } } @@ -343,14 +348,16 @@ class LineChart extends Component { return (
- - + + + +
From 535ec607a5c65289e589610b09a37d312cf8cae4 Mon Sep 17 00:00:00 2001 From: "yeosong.heo" Date: Thu, 24 Oct 2019 23:46:52 +0900 Subject: [PATCH 59/77] :bug: xlogflow resize event remove --- .../Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js index 3ea6611..7106fdc 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.js @@ -161,6 +161,7 @@ class XlogFlow extends Component { componentWillReceiveProps(nextProps){ // console.log("componentWillReceiveProps"); + window.removeEventListener("resize", this.resize); } resize = () =>{ From d63c6965023c0daaf0b679ac7b839798d0aa6ae4 Mon Sep 17 00:00:00 2001 From: "yeosong.heo" Date: Fri, 25 Oct 2019 08:04:41 +0900 Subject: [PATCH 60/77] :construction: line chart refactoring line fill --- src/components/Paper/LineChart/Line.js | 299 +++++++++++++++++--- src/components/Paper/LineChart/LineChart.js | 63 ++++- 2 files changed, 314 insertions(+), 48 deletions(-) diff --git a/src/components/Paper/LineChart/Line.js b/src/components/Paper/LineChart/Line.js index ceef5ae..e998819 100644 --- a/src/components/Paper/LineChart/Line.js +++ b/src/components/Paper/LineChart/Line.js @@ -10,12 +10,13 @@ import connect from "react-redux/es/connect/connect"; import {withRouter} from "react-router-dom"; import * as d3 from "d3"; import numeral from "numeral"; +import InstanceColor from "../../../common/InstanceColor"; class Line extends Component { state = { g : null, }; - + isInit = false; constructor(props) { super(props); @@ -23,21 +24,237 @@ class Line extends Component { componentWillReceiveProps(nextProps){ - // console.log("first read;;",nextProps.options,this.props.options); - // console.log("componentWillReceiveProps ====================>",this.state.g); + if(!this.isInit){ + return; + } if( nextProps.options !== this.props.options){ - //- init - console.log('option changed.'); - // this.prepare(null,nextProps.options); + this.changedOption(nextProps.options,nextProps); } + this.paint(nextProps); }; + paint (data){ + + + if (data.objects) { + let instanceMetricCount = {}; + for (let counterKey in data.counters) { + let thisOption = data.box.option.filter((d) => {return d.counterKey === counterKey})[0]; + if (!thisOption) { + for (let i = 0; i < data.objects.length; i++) { + this.removeCounterLine(data.objects[i], counterKey); + } + } else if(data.options.type ==='STACK AREA') { + // this.drawStackLine(thisOption,counterKey); + } else { + for (let i = 0; i < data.objects.length; i++) { + const obj = data.objects[i]; + + if (obj.objFamily === thisOption.familyName) { + if (!instanceMetricCount[obj.objHash]) { + instanceMetricCount[obj.objHash] = 0; + } + let color; + if (data.config.graph.color === "metric") { + color = InstanceColor.getMetricColor(thisOption.counterKey, data.config.colorType); + } else { + color = InstanceColor.getInstanceColors(data.config.colorType)[obj.objHash][(instanceMetricCount[obj.objHash]++) % 5]; + } + this.drawLine(obj, thisOption, counterKey, color,data); + } + } + } + } + } + }; + + replaceAll(str, searchStr, replaceStr) { + return str.split(searchStr).join(replaceStr); + } + + replaceName (name) { + if (name) { + return this.replaceAll(this.replaceAll(name, "%", "_PCT_"), '$', '_DOLLAR_'); + } + return name; + } + removeCounterLine(obj, counterKey) { + let pathClass = "line-" + obj.objHash + "-" + this.replaceName(counterKey); + let path = this.line.selectAll("path." + pathClass); + + // 라인 그래프 삭제 + if (path && path.size() > 0) { + path.remove(); + } + + // 툴팁 그래프 삭제 + let circleKey = "circle-" + obj.objHash + "_" + this.replaceName(counterKey); + let circle = this.focus.selectAll("circle." + circleKey); + + if (circle.size() > 0) { + circle.remove(); + } + // 제목 삭제 + this.props.removeTitle(counterKey); + }; + + drawLine = (obj, option, counterKey, color,data) => { + if (this.props.box.values['chartType'] === "LINE FILL") { + + let areaClass = "area-" + obj.objHash + "-" + this.replaceName(counterKey); + let area = this.line.selectAll("path." + areaClass) + + + if (area.size() < 1) { + area = this.line.insert("path", ":first-child").attr("class", areaClass).style("stroke", color); + } + + let valueArea = d3.area().curve(d3[this.props.config.graph.curve]) + .x((d) =>this.xScale(d.time)) + .y0(data.options.height) + .y1((counter) =>{ + let objData = counter.data[obj.objHash]; + if (objData) { + return this.yScale(objData.value); + } else { + return this.yScale(0); + } + }); + + + if (data.config.graph.break === "Y") { + valueArea.defined((d)=> { + let objData = d.data ? d.data[obj.objHash] : null; + return objData && !isNaN(d.time) && !isNaN(objData.value) && !isNaN(this.yScale(objData.value)); + }) + } + + + if (!this.props.filterMap[obj.objHash]) { + area.data([data.counters[counterKey]]) + .attr("d", valueArea) + .style("fill", color) + .style("opacity", 0) + .transition() + .delay(100); + } else { + area.data([data.counters[counterKey]]) + .attr("d", valueArea) + .style("fill", color) + .style("opacity", data.config.graph.fillOpacity) + .transition() + .delay(100); + } + } + + + + let pathClass = `line-${obj.objHash}-${this.replaceName(counterKey)}`; + let path = this.line.selectAll("path." + pathClass); + + if (path.size() < 1) { + path = this.line.insert("path", ":first-child").attr("class", pathClass).style("stroke", color); + if (this.props.config.graph.color === "instance") { + if (this.props.config.colorType === "white") { + this.props.setTitle(counterKey, option.title, "#333", option.familyName); + } else { + this.props.setTitle(counterKey, option.title, "white", option.familyName); + } + } else { + this.props.setTitle(counterKey, option.title, color, option.familyName); + } + } else { + path.style("stroke", color); + if (this.props.config.graph.color === "instance") { + if (this.props.config.colorType === "white") { + this.props.setTitle(counterKey, option.title, "#333", option.familyName); + } else { + this.props.setTitle(counterKey, option.title, "white", option.familyName); + } + } else { + this.props.setTitle(counterKey, option.title, color, option.familyName); + } + } + + let valueLine = d3.line().curve(d3[this.props.config.graph.curve]); + + if (this.props.config.graph.break === "Y") { + valueLine.defined((d) => { + let objData = d.data ? d.data[obj.objHash] : null; + return objData && !isNaN(d.time) && !isNaN(objData.value) && !isNaN(this.yScale(objData.value)); + }) + } + + valueLine.x( (d)=> { + return this.xScale(d.time); + }).y((counter) => { + let objData = counter.data[obj.objHash]; + if (objData) { + return this.yScale(objData.value); + } else { + return this.yScale(0); + } + }); + + if (!this.props.filterMap[obj.objHash]) { + this.setAnimation(path.data([data.counters[counterKey]])).attr("d", valueLine).style("stroke-width", this.props.config.graph.width).style("opacity", 0); + } else { + this.setAnimation(path.data([data.counters[counterKey]])).attr("d", valueLine).style("stroke-width", this.props.config.graph.width).style("opacity", this.props.config.graph.opacity); + } + }; + setAnimation(svg){ + const {realTime} = this.props.range; + return realTime ? svg : svg.transition().duration(500); + } + changedOption(changed,props){ + + this.area_clip + .attr("width", changed.width) + .attr("height", changed.height); + + this.xScale = this.xScale.range([0, changed.width]); + this.yScale = this.yScale.range([changed.height, 0]); + + this.xScale.domain([props.startTime,props.endTime]); + this.yScale.domain([0, changed.maxY]); + + + let xAxisCount = Math.floor(changed.width / changed.xAxisWidth); + if (xAxisCount < 1) { + xAxisCount = 1; + } + let yAxisCount = Math.floor(changed.height / changed.yAxisHeight); + if (yAxisCount < 1) { + yAxisCount = 1; + } +// Y축 + this.tickY.ticks(yAxisCount); + + this.gridTickY.tickSize(-changed.width) + .ticks(yAxisCount); + + this.axisY.transition().duration(500).call(this.tickY); + this.gridY.transition().duration(500).call(this.gridTickY); +//- X축 + this.tickX.tickFormat(d3.timeFormat(changed.timeFormat)) + .ticks(xAxisCount); + + this.gridTickX.tickSize(-changed.height) + .ticks(xAxisCount); + + this.axisX.attr("transform", `translate(0,${changed.height})`) + .call(this.tickX); + this.gridX.attr("transform", `translate(0,${changed.height})`) + .call(this.gridTickX); + + } zoomBrush = () => { const extent = d3.event.selection; const {realTime} = this.props.range; }; prepare(g){ + const {width,height,margin} = this.props.options; const {options} = this.props; // const {svg}= this.state; @@ -45,7 +262,7 @@ class Line extends Component { // console.log('',svg,margin,width,height); this.svg = d3.select(g.parentNode); - this.svg.append("defs") + this.area_clip = this.svg.append("defs") .append("svg:clipPath") .attr("id", `area-clip${this.props.box.key}`) .append("svg:rect") @@ -73,11 +290,11 @@ class Line extends Component { this.top.append("g").attr("class", "brush").call(this.brush); // // //Axis Draw - this.xAxis = d3.scaleTime().range([0, width]); - this.yAxis = d3.scaleLinear().range([height, 0]); + this.xScale = d3.scaleTime().range([0, width]); + this.yScale = d3.scaleLinear().range([height, 0]); // - this.xAxis.domain([this.props.startTime, this.props.endTime]); - this.yAxis.domain([0, options.maxY]); + this.xScale.domain([this.props.startTime, this.props.endTime]); + this.yScale.domain([0, options.maxY]); // let xAxisCount = Math.floor(width / options.xAxisWidth); if (xAxisCount < 1) { @@ -87,36 +304,48 @@ class Line extends Component { if (yAxisCount < 1) { yAxisCount = 1; } +// Y축 + this.tickY = d3.axisLeft(this.yScale) + .tickFormat((d)=>numeral(d).format('0.0a')); - this.top.insert("g", ":first-child").attr("class", "axis-y") - .call(d3.axisLeft(this.yAxis) - .tickFormat((d)=>numeral(d).format('0.0a')).ticks(yAxisCount)); + this.tickY.ticks(yAxisCount); - this.top.insert("g", ":first-child") - .attr("class", "grid-y") - .style("stroke-dasharray", "5 2") - .style("opacity", options.opacity) - .call(d3.axisLeft(this.yAxis) - .tickSize(-options.width) - .tickFormat("").ticks(yAxisCount)); + this.axisY = this.top.insert("g", ":first-child").attr("class", "axis-y") + .call(this.tickY); - this.top.insert("g", ":first-child") + this.gridTickY = d3.axisLeft(this.yScale) + .tickSize(-options.width) + .tickFormat(""); + this.gridTickY.ticks(yAxisCount); + + this.gridY = this.top.insert("g", ":first-child") + .attr("class", "grid-y") + .style("stroke-dasharray", "5 2") + .style("opacity", options.opacity) + .call(this.gridTickY); +//- X축 + this.tickX = d3.axisBottom(this.xScale) + .tickFormat(d3.timeFormat(options.timeFormat)); + + this.tickX.ticks(xAxisCount); + this.axisX = this.top.insert("g", ":first-child") .attr("class", "axis-x") .attr("transform", "translate(0," + height + ")") - .call(d3.axisBottom(this.xAxis) - .tickFormat(d3.timeFormat(options.timeFormat)) - .ticks(xAxisCount)); - - this.top.insert("g", ":first-child") - .attr("class", "grid-x") - .style("stroke-dasharray", "5 2") - .style("opacity", options.opacity) - .attr("transform", `translate(0,${options.height})`) - .call(d3.axisBottom(this.xAxis) - .tickSize(-options.height) - .tickFormat("") - .ticks(xAxisCount)); + .call(this.tickX); + + this.gridTickX = d3.axisBottom(this.xScale) + .tickSize(-options.height) + .tickFormat(""); + + this.gridTickX.ticks(xAxisCount); + this.gridX = this.top.insert("g", ":first-child") + .attr("class", "grid-x") + .style("stroke-dasharray", "5 2") + .style("opacity", options.opacity) + .attr("transform", `translate(0,${options.height})`) + .call(this.gridTickX); + this.isInit = true; }; componentDidMount() { diff --git a/src/components/Paper/LineChart/LineChart.js b/src/components/Paper/LineChart/LineChart.js index b4ae38c..7569212 100644 --- a/src/components/Paper/LineChart/LineChart.js +++ b/src/components/Paper/LineChart/LineChart.js @@ -54,20 +54,21 @@ class LineChart extends Component { } resize(){ - console.log('resize....'); - let box = this.refs.lineChart.parentNode.parentNode.parentNode.parentNode; - this.graph.width = box.offsetWidth - this.graph.margin.left - this.graph.margin.right; - this.graph.height = box.offsetHeight - this.graph.margin.top - this.graph.margin.bottom - 27; - this.setState({ - options : {...this.graph, type : this.chartType} - }); + + if(this.refs) { + let box = this.refs.lineChart.parentNode.parentNode.parentNode.parentNode; + this.graph.width = box.offsetWidth - this.graph.margin.left - this.graph.margin.right; + this.graph.height = box.offsetHeight - this.graph.margin.top - this.graph.margin.bottom - 27; + this.setState({ + options: {...this.graph, type: this.chartType} + }); + } } componentWillUnmount() { - window.removeEventListener("resize", this.resize); + }; componentDidMount() { - window.addEventListener("resize", this.resize); if (this.props.config.colorType === "white") { this.graph.opacity = 0.3; } else { @@ -203,7 +204,8 @@ class LineChart extends Component { startTime: startTime, endTime: endTime, counters: counters, - noData: noData + noData: noData, + options : {...this.graph,type : this.chartType} }); } @@ -333,16 +335,50 @@ class LineChart extends Component { }; axisUp = () => { - + this.graph.maxY = this.graph.maxY * 1.2; + this.setState({ + options: {...this.graph} + }); }; axisDown = () => { - + this.graph.maxY = this.graph.maxY * 0.8; + if (this.graph.maxY < this.graph.autoMaxY) { + this.graph.maxY = this.graph.autoMaxY; + } + this.setState({ + options: {...this.graph} + }); }; stopProgation = (e) => { e.stopPropagation(); }; + componentDidUpdate = (prevProps, prevState) => { + + if(this.isChangedSize()){ + this.resize(); + } + + // if (prevProps.layoutChangeTime !== this.props.layoutChangeTime) { + // if(this.isChangedSize()){ + // this.resize(); + // } + // }else{ + // // this.graphResize(); + // // this.removeObjLine(prevProps.objects, this.props.objects); + // + // } + }; + + isChangedSize(){ + const box = this.refs.lineChart.parentNode.parentNode.parentNode.parentNode; + if ((box.offsetWidth - this.graph.margin.left - this.graph.margin.right !== this.graph.width) || (this.graph.height !== box.offsetHeight - this.graph.margin.top - this.graph.margin.bottom - 27)) { + return true; + } + return false; + + } render() { return ( @@ -355,7 +391,8 @@ class LineChart extends Component { noData={this.state.noData} options={this.state.options} box = {this.props.box} - > + setTitle={this.props.setTitle} + >\
From 81d5d7cd54992501209d4d9637a56b88828280b7 Mon Sep 17 00:00:00 2001 From: kranian Date: Fri, 25 Oct 2019 18:59:00 +0900 Subject: [PATCH 61/77] :construction: line chart resize , counter option, chart type change restructure --- src/components/Paper/LineChart/Line.js | 612 +++++++++++++++++--- src/components/Paper/LineChart/LineChart.js | 96 ++- 2 files changed, 596 insertions(+), 112 deletions(-) diff --git a/src/components/Paper/LineChart/Line.js b/src/components/Paper/LineChart/Line.js index e998819..fc480bd 100644 --- a/src/components/Paper/LineChart/Line.js +++ b/src/components/Paper/LineChart/Line.js @@ -9,6 +9,7 @@ import { import connect from "react-redux/es/connect/connect"; import {withRouter} from "react-router-dom"; import * as d3 from "d3"; +import * as _ from "lodash"; import numeral from "numeral"; import InstanceColor from "../../../common/InstanceColor"; @@ -27,59 +28,133 @@ class Line extends Component { if(!this.isInit){ return; } + + if(nextProps.search !== this.props.search){ + switch(this.props.options.type){ + case "LINE FILL": + case "LINE": + this.removePathLine(true); + break; + case "STACK AREA": + this.removePathLine(false); + break; + } + } + + const {type} = nextProps.options; + const thisType = this.props.options.type; + + if( type !== thisType){ + switch(type){ + case "LINE FILL": + case "LINE": + this.removePathLine(true); + break; + case "STACK AREA": + this.removePathLine(false); + break; + } + } + if( nextProps.options !== this.props.options){ this.changedOption(nextProps.options,nextProps); } this.paint(nextProps); }; + removePathLine(isStack){ + if(!isStack) { + for (const obj of this.props.objects) { + + for (let counterKey in this.props.counters) { + let areaClass = "area-" + obj.objHash + "-" + this.replaceName(counterKey); + let lineClass = "line-" + obj.objHash + "-" + this.replaceName(counterKey); + this.line.selectAll("path." + areaClass) + .transition() + .delay(100) + .remove(); + this.line.selectAll("path." + lineClass) + .transition() + .delay(100) + .remove(); + } + } + }else{ + this.stackArea.selectAll('path.line') + .transition() + .delay(100) + .remove(); + } + } paint (data){ - + this.clearLine(); if (data.objects) { let instanceMetricCount = {}; for (let counterKey in data.counters) { let thisOption = data.box.option.filter((d) => {return d.counterKey === counterKey})[0]; - if (!thisOption) { - for (let i = 0; i < data.objects.length; i++) { - this.removeCounterLine(data.objects[i], counterKey); - } - } else if(data.options.type ==='STACK AREA') { - // this.drawStackLine(thisOption,counterKey); - } else { - for (let i = 0; i < data.objects.length; i++) { - const obj = data.objects[i]; - - if (obj.objFamily === thisOption.familyName) { - if (!instanceMetricCount[obj.objHash]) { - instanceMetricCount[obj.objHash] = 0; - } - let color; - if (data.config.graph.color === "metric") { - color = InstanceColor.getMetricColor(thisOption.counterKey, data.config.colorType); - } else { - color = InstanceColor.getInstanceColors(data.config.colorType)[obj.objHash][(instanceMetricCount[obj.objHash]++) % 5]; + if(thisOption){ + switch(data.options.type){ + case 'STACK AREA': + this.drawStackArea(thisOption,counterKey,data); + break; + default : + //- LINE,LINEFILL + for (let i = 0; i < data.objects.length; i++) { + const obj = data.objects[i]; + if (obj.objFamily === thisOption.familyName) { + if (!instanceMetricCount[obj.objHash]) { + instanceMetricCount[obj.objHash] = 0; + } + let color; + if (data.config.graph.color === "metric") { + color = InstanceColor.getMetricColor(thisOption.counterKey, data.config.colorType); + } else { + color = InstanceColor.getInstanceColors(data.config.colorType)[obj.objHash][(instanceMetricCount[obj.objHash]++) % 5]; + } + this.drawLine(obj, thisOption, counterKey, color,data); + } } - this.drawLine(obj, thisOption, counterKey, color,data); - } - } + + + } } } } + this.moveTooltip(); + this.drawTimeFocus(data.timeFocus.keep,data); + + }; + + moveTooltip = () => { + if (this.currentTooltipTime) { + let xPosition = this.xScale(this.currentTooltipTime); + this.focus.selectAll("circle").attr("cx", xPosition); + + let hoverLine = this.focus.select("line.x-hover-line"); + hoverLine.attr("x1", xPosition); + hoverLine.attr("x2", xPosition); + } }; + clearLine(){ + _.forEach(this.props.removeCounter,d=>this.removeCounterLine(d.key,d.counter)); + _.forEach(this.props.removeObject, d=>this.removeCounterLine(d.key,d.counter)); + } + replaceAll(str, searchStr, replaceStr) { return str.split(searchStr).join(replaceStr); } + replaceName (name) { if (name) { return this.replaceAll(this.replaceAll(name, "%", "_PCT_"), '$', '_DOLLAR_'); } return name; } - removeCounterLine(obj, counterKey) { - let pathClass = "line-" + obj.objHash + "-" + this.replaceName(counterKey); + removeCounterLine(objHash, counterKey) { + let pathClass = "line-" + objHash + "-" + this.replaceName(counterKey); let path = this.line.selectAll("path." + pathClass); // 라인 그래프 삭제 @@ -88,7 +163,7 @@ class Line extends Component { } // 툴팁 그래프 삭제 - let circleKey = "circle-" + obj.objHash + "_" + this.replaceName(counterKey); + let circleKey = "circle-" + objHash + "_" + this.replaceName(counterKey); let circle = this.focus.selectAll("circle." + circleKey); if (circle.size() > 0) { @@ -98,17 +173,159 @@ class Line extends Component { this.props.removeTitle(counterKey); }; - drawLine = (obj, option, counterKey, color,data) => { - if (this.props.box.values['chartType'] === "LINE FILL") { + drawTimeFocus=(isFixed=false,nextProps)=>{ + + if( isFixed && !this.props.noData){ + if( Object.keys(this.props.counters).map(k => this.props.counters[k][0] ? this.props.counters[k][0].time : null).filter( t => this.props.timeFocus.time > t ).length ) { + let hoverLine = this.focus.selectAll("line.focus-line"); + hoverLine.attr("x1", (d) => this.xScale(d)) + .attr("x2", (d) => this.xScale(d)); + + hoverLine.data([this.props.timeFocus.time]) + .enter() + .append("line") + .attr("class", "focus-line focus-hover-line") + .attr("y1", 0) + .attr("y2", nextProps.options.height) + .attr("x1", (d) => { + return this.xScale(d); + }) + .attr("x2", (d) => this.xScale(d)) + .exit() + .remove(); + }else{ + // 해제 + this.props.setTimeFocus(false,null,null,false); + } - let areaClass = "area-" + obj.objHash + "-" + this.replaceName(counterKey); - let area = this.line.selectAll("path." + areaClass) + }else if( !this.props.noData + && this.props.active + && this.props.timeFocus.id !== this.props.box.key) { + let hoverLine = this.focus.selectAll("line.focus-line"); + hoverLine.attr("x1", (d) =>this.xScale(d)) + .attr("x2", (d) =>this.xScale(d)); + + hoverLine.data([this.props.timeFocus.time]) + .enter() + .append("line") + .attr("class", "focus-line focus-hover-line") + .attr("y1", 0) + .attr("y2", nextProps.options.height) + .attr("x1", (d) =>{ + return this.xScale(d); + }) + .attr("x2", (d) =>this.xScale(d)) + .exit() + .remove(); + }else{ + this.focus.select("line.focus-line").remove(); + } + }; + drawStackArea=(thisOption,counterKey,data) => { + let instanceMetricCount = {}; + const color = {}; + //- instance color making + for (const attr in this.props.objects) { + const _obj = this.props.objects[attr]; + if (_obj.objFamily === thisOption.familyName) { + if (!instanceMetricCount[_obj.objHash]) { + instanceMetricCount[_obj.objHash] = 0; + } + if (this.props.config.graph.color === "metric") { + const _cl = InstanceColor.getMetricColor(thisOption.counterKey, this.props.config.colorType); + color[_obj.objHash] = _cl; + } else { + const _cl = InstanceColor.getInstanceColors(this.props.config.colorType)[_obj.objHash][(instanceMetricCount[_obj.objHash]++) % 5]; + color[_obj.objHash] = _cl; + } + } + } + //- instance data flat data making + const stackData = _(data.counters[counterKey]) + .map((d) => { + const _r = Object.keys(d.data).map(key => { + const _keyValue = []; + _keyValue['objHash'] = d.data[key].objHash; + _keyValue['time'] = d.time; + _keyValue['value'] = Number(d.data[key].value); + _keyValue['color'] = color[d.data[key].objHash]; + return _keyValue; + }); + return _r; + }).flatMapDepth().value(); + //- 인스턴스 별 데이터로 변환 + let ld = d3.nest().key(d => d.objHash).entries(stackData); + const _sort = []; + + //- 인스턴스 순서 정렬 + for (const attr in this.props.objects) { + const _obj = this.props.objects[attr]; + const _find = _.findIndex(ld, (o) => o.key === _obj.objHash); + if(_find > -1 ){ + _sort.push(ld[_find]); + } + } + //- 인스턴스 그리기 + const area = d3.area().curve(d3[this.props.config.graph.curve]) + .x(d =>{ + return this.xScale(d[0]); + }) + .y0(d => this.yScale(d[2])) + .y1(d => this.yScale(d[1])); - if (area.size() < 1) { - area = this.line.insert("path", ":first-child").attr("class", areaClass).style("stroke", color); - } + if (this.props.config.graph.break === "Y") { + area.defined((d)=>{ + return !isNaN(d[0]) && !isNaN(d[1]) && !isNaN(d[2]); + }) + } + + //- 시간 별 Y축 데이터 어그리게이션 + let pre = {}; + //- 차트 갱신 + let paintGroup = this.stackArea.selectAll("path.line") + .data(_sort) + .attr("d",(d)=> { + const _d = _.map(d.values,(_node) =>{ + const _key = _node.time; + const pre_v = pre[_key] ? pre[_key] : 0; + const next_v = pre_v + Number(_node.value); + pre[_key] = next_v; + return [_node.time,next_v,pre_v]; + }); + return area(_d); + }); + + //- 차트 생성 + paintGroup.enter() + .append('path') + .attr("d",(d)=> { + const _d = _.map(d.values,(_node) =>{ + const _key = _node.time; + const pre_v = pre[_key] ? pre[_key] : 0; + const next_v = pre_v + _node.value; + pre[_key] = next_v; + return [_node.time,next_v,pre_v]; + }); + return area(_d); + }) + .attr('class',(d)=> `line ${d.key}` ) + .attr('data-col-name', (d)=> d.key) + .style("fill", (d)=> { + return color[d.key]; + }) + .attr("fill-opacity", this.props.config.graph.fillOpacity) + .attr("stroke",(d) =>{ + return color[d.key]; + }) + .style("stroke-width", this.props.config.graph.width) + .style("opacity", this.props.config.graph.opacity); + //- 차트 갱신 후 데이터 삭제 + paintGroup.exit().remove(); + }; + drawLine = (obj, option, counterKey, color,data) => { + if (this.props.box.values['chartType'] === "LINE FILL") { let valueArea = d3.area().curve(d3[this.props.config.graph.curve]) .x((d) =>this.xScale(d.time)) .y0(data.options.height) @@ -120,8 +337,6 @@ class Line extends Component { return this.yScale(0); } }); - - if (data.config.graph.break === "Y") { valueArea.defined((d)=> { let objData = d.data ? d.data[obj.objHash] : null; @@ -129,54 +344,25 @@ class Line extends Component { }) } + let areaClass = "area-" + obj.objHash + "-" + this.replaceName(counterKey); + let area = this.line.selectAll("path." + areaClass) + .data([data.counters[counterKey]]) + .attr("d",valueArea); + area= area.enter() + .insert('path') + .attr("class",areaClass) + .style("stroke", color) + .style("fill", color) + .style("opacity", !this.props.filterMap[obj.objHash] ? 0 : data.config.graph.fillOpacity); - if (!this.props.filterMap[obj.objHash]) { - area.data([data.counters[counterKey]]) - .attr("d", valueArea) - .style("fill", color) - .style("opacity", 0) - .transition() - .delay(100); - } else { - area.data([data.counters[counterKey]]) - .attr("d", valueArea) - .style("fill", color) - .style("opacity", data.config.graph.fillOpacity) - .transition() - .delay(100); - } - } - - + area.exit().remove(); - let pathClass = `line-${obj.objHash}-${this.replaceName(counterKey)}`; - let path = this.line.selectAll("path." + pathClass); + area.transition() + .delay(100) - if (path.size() < 1) { - path = this.line.insert("path", ":first-child").attr("class", pathClass).style("stroke", color); - if (this.props.config.graph.color === "instance") { - if (this.props.config.colorType === "white") { - this.props.setTitle(counterKey, option.title, "#333", option.familyName); - } else { - this.props.setTitle(counterKey, option.title, "white", option.familyName); - } - } else { - this.props.setTitle(counterKey, option.title, color, option.familyName); - } - } else { - path.style("stroke", color); - if (this.props.config.graph.color === "instance") { - if (this.props.config.colorType === "white") { - this.props.setTitle(counterKey, option.title, "#333", option.familyName); - } else { - this.props.setTitle(counterKey, option.title, "white", option.familyName); - } - } else { - this.props.setTitle(counterKey, option.title, color, option.familyName); - } } - let valueLine = d3.line().curve(d3[this.props.config.graph.curve]); + const valueLine = d3.line().curve(d3[this.props.config.graph.curve]); if (this.props.config.graph.break === "Y") { valueLine.defined((d) => { @@ -196,18 +382,40 @@ class Line extends Component { } }); - if (!this.props.filterMap[obj.objHash]) { - this.setAnimation(path.data([data.counters[counterKey]])).attr("d", valueLine).style("stroke-width", this.props.config.graph.width).style("opacity", 0); + let pathClass = `line-${obj.objHash}-${this.replaceName(counterKey)}`; + let path = this.line.selectAll("path." + pathClass) + .data([data.counters[counterKey]]) + .attr("d",valueLine); + + path = path.enter() + .insert("path") + .attr("class",pathClass) + .style("stroke", color) + .style("stroke-width", this.props.config.graph.width) + .style("opacity", !this.props.filterMap[obj.objHash] ? 0 : this.props.config.graph.opacity); + this.setAnimation(path) + + path.exit().remove(); + + + if (this.props.config.graph.color === "instance") { + if (this.props.config.colorType === "white") { + this.props.setTitle(counterKey, option.title, "#333", option.familyName); + } else { + this.props.setTitle(counterKey, option.title, "white", option.familyName); + } } else { - this.setAnimation(path.data([data.counters[counterKey]])).attr("d", valueLine).style("stroke-width", this.props.config.graph.width).style("opacity", this.props.config.graph.opacity); + this.props.setTitle(counterKey, option.title, color, option.familyName); } + }; setAnimation(svg){ const {realTime} = this.props.range; return realTime ? svg : svg.transition().duration(500); } changedOption(changed,props){ - + this.brush.extent([[0, 0], [changed.width, changed.height]]); + this.brushG.call(this.brush); this.area_clip .attr("width", changed.width) .attr("height", changed.height); @@ -251,8 +459,59 @@ class Line extends Component { zoomBrush = () => { const extent = d3.event.selection; const {realTime} = this.props.range; + + // this.focus.selectAll("circle").style("display", "none"); + // this.focus.select("line.x-hover-line").style("display", "none"); + }; + + mouseOverObject = (obj, thisOption, color) => { + + let r = 3; + + let circleKey = "circle-" + obj.objHash + "_" + this.replaceName(thisOption.counterKey); + let circle = this.focus.select("circle." + circleKey); + if (circle.size() < 1) { + circle = this.focus.append("circle").attr("class", circleKey).attr("r", r).attr("stroke", color); + } + + if (this.props.filterMap[obj.objHash]) { + circle.style("opacity", 1); + } else { + circle.style("opacity", 0); + } + }; + mouseMoveObject = (obj, thisOption, counterKey, dataIndex, color, tooltip) => { + + + let circleKey = "circle-" + obj.objHash + "_" + this.replaceName(thisOption.counterKey); + let unit = this.props.counters[counterKey][dataIndex].data[obj.objHash] ? this.props.counters[counterKey][dataIndex].data[obj.objHash].unit : ""; + let valueOutput = obj.objHash && this.props.counters[counterKey][dataIndex].data[obj.objHash] ? this.props.counters[counterKey][dataIndex].data[obj.objHash].value : null ; + const valueOrigin = obj.objHash && this.props.counters[counterKey][dataIndex].data[obj.objHash] ? this.props.counters[counterKey][dataIndex].data[obj.objHash].value : null ; + if( this.chartType === "STACK AREA" && valueOutput ){ + valueOutput = this.counterSum + valueOutput; + this.counterSum = valueOutput; + } + + if (this.props.counters[counterKey][dataIndex].time) { + if (this.props.filterMap[obj.objHash]) { + tooltip.lines.push({ + instanceName: obj.objName, + circleKey: circleKey, + metricName: thisOption.title, + value: valueOutput ? valueOutput : null, + displayValue: valueOrigin ? numeral(valueOrigin).format(this.props.config.numberFormat) + " " + unit : null, + color: color + }); + } + } else { + this.focus.select("circle." + circleKey).style("display", "none"); + } + + return true; + }; + prepare(g){ const {width,height,margin} = this.props.options; @@ -262,6 +521,7 @@ class Line extends Component { // console.log('',svg,margin,width,height); this.svg = d3.select(g.parentNode); + this.area_clip = this.svg.append("defs") .append("svg:clipPath") .attr("id", `area-clip${this.props.box.key}`) @@ -285,9 +545,15 @@ class Line extends Component { this.brush = d3.brushX() .extent([[0, 0], [width, height]]) + .on("start",()=>{ + // this.props.hideTooltip(); + this.focus.selectAll("circle").style("display", "none"); + this.focus.selectAll("line").style("display", "none"); + }) .on("end", this.zoomBrush); - this.top.append("g").attr("class", "brush").call(this.brush); + this.brushG = this.top.append("g").attr("class", "brush") + .call(this.brush); // // //Axis Draw this.xScale = d3.scaleTime().range([0, width]); @@ -345,6 +611,186 @@ class Line extends Component { .style("opacity", options.opacity) .attr("transform", `translate(0,${options.height})`) .call(this.gridTickX); +// event setting + + this.svg.on("mouseover", ()=> { + let layer = g.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode; + layer.style.zIndex = 9; + + let hoverLine = this.focus.select("line.x-hover-line"); + if (hoverLine.size() > 0) { + hoverLine.style("display", "block"); + } + + let instanceMetricCount = {}; + for (let counterKey in this.props.counters) { + let thisOption = this.props.box.option.filter((d) => {return d.counterKey === counterKey})[0]; + if (!thisOption) { + break; + } + + for (let i = 0; i < this.props.objects.length; i++) { + const obj = this.props.objects[i]; + if (thisOption.familyName === obj.objFamily) { + if (!instanceMetricCount[obj.objHash]) { + instanceMetricCount[obj.objHash] = 0; + } + let color; + if (this.props.config.graph.color === "metric") { + color = InstanceColor.getMetricColor(thisOption.counterKey, this.props.config.colorType); + } else { + color = InstanceColor.getInstanceColors(this.props.config.colorType)[obj.objHash][(instanceMetricCount[obj.objHash]++) % 5]; + } + this.mouseOverObject(this.props.objects[i], thisOption, color); + } + } + } + + this.focus.selectAll("circle").style("display", "block"); + }); + + + + this.svg.on("mouseout",() =>{ + + let layer = g.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode; + layer.style.zIndex = 5; + this.focus.selectAll("circle").style("display", "none"); + this.focus + .select("line.x-hover-line") + .style("display", "none"); + + this.props.hideTooltip(); + this.currentTooltipTime = null; + // if(!this.props.timeFocus.keep) + // this.props.setTimeFocus(false, null, this.props.box.key); + + }); + this.bisector = d3.bisector(function (d) { + return d.time; + }).left; + + const that = this; + this.svg.on("mousemove", function(){ + + let tooltip = {}; + tooltip.lines = []; + let xPos = d3.mouse(this)[0] - that.props.options.margin.left; + let yPos = d3.mouse(this)[1]; + if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) { + let box = g.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode; + if (window.getComputedStyle) { + let style = getComputedStyle(box); + let transform = style.transform || style.webkitTransform || style.mozTransform; + let mat = transform.match(/^matrix3d\((.+)\)$/); + if (mat) return parseFloat(mat[1].split(', ')[13]); + mat = transform.match(/^matrix\((.+)\)$/); + let transformX = mat ? parseFloat(mat[1].split(', ')[4]) : 0; + let transformY = mat ? parseFloat(mat[1].split(', ')[5]) : 0; + xPos = xPos - transformX; + yPos = yPos - transformY; + } + } + + let x0 = that.xScale.invert(xPos); + let timeFormat = d3.timeFormat(that.props.options.fullTimeFormat); + + let instanceMetricCount = {}; + + for (let counterKey in that.props.counters) { + let thisOption = that.props.box.option.filter((d) => {return d.counterKey === counterKey})[0]; + let dataIndex = that.bisector(that.props.counters[counterKey], x0, 0); + + if (!that.props.counters[counterKey][dataIndex]) { + break; + } + + if (tooltip.timeValue && (tooltip.timeValue < that.props.counters[counterKey][dataIndex].time)) { + + } else { + tooltip.time = timeFormat(that.props.counters[counterKey][dataIndex].time); + tooltip.timeValue = that.props.counters[counterKey][dataIndex].time; + } + + if (!thisOption) { + break; + } + that.counterSum = 0; + for (let i = 0; i < that.props.objects.length; i++) { + const obj = that.props.objects[i]; + if (thisOption.familyName === obj.objFamily) { + + if (!instanceMetricCount[obj.objHash]) { + instanceMetricCount[obj.objHash] = 0; + } + let color; + if (that.props.config.graph.color === "metric") { + color = InstanceColor.getMetricColor(thisOption.counterKey, that.props.config.colorType); + } else { + color = InstanceColor.getInstanceColors(that.props.config.colorType)[obj.objHash][(instanceMetricCount[obj.objHash]++) % 5]; + } + that.mouseMoveObject(that.props.objects[i], thisOption, counterKey, dataIndex, color, tooltip); + } + } + } + + let hoverLine = that.focus.select("line.x-hover-line").style('display','block'); + if (hoverLine.size() < 1) { + hoverLine = that.focus.append("line").attr("class", "x-hover-line hover-line").attr("y1", 0).attr("y2", that.props.options.height); + } + + let xPosition = that.xScale(tooltip.timeValue); + + if (tooltip.timeValue) { + hoverLine.attr("x1", xPosition); + hoverLine.attr("x2", xPosition); + } + + if (tooltip && tooltip.lines) { + for (let i = 0; i < tooltip.lines.length; i++) { + + if (!isNaN(tooltip.lines[i].value)) { + let circle = that.focus.select("circle." + tooltip.lines[i].circleKey).style('display','block'); + if (circle.size() > 0) { + circle.attr("cx", xPosition); + circle.attr("cy", that.yScale(tooltip.lines[i].value)); + } + } + } + } + + tooltip.chartType = that.props.options.type; + tooltip.counterSum = numeral(that.counterSum).format(that.props.config.numberFormat); + that.currentTooltipTime = tooltip.timeValue; + + // if(!that.props.timeFocus.keep){ + // that.props.setTimeFocus(true,x0.getTime(),that.props.box.key); + // } + + that.props.showTooltip(xPos, yPos, that.props.options.margin.left, that.props.options.margin.top, tooltip); + + + }); + + this.svg.on("contextmenu",()=>{ + // console.log(d3.event.which); + d3.event.preventDefault(); + // e.preventDefault(); + if(!this.props.timeFocus.keep){ + //toggle + //tooltip hidel + this.focus.select("line.x-hover-line").style("display","none"); + this.focus.selectAll("circle").style("display","none"); + this.props.hideTooltip(); + } + this.props.setTimeFocus( + this.props.timeFocus.active, + this.props.timeFocus.time, + this.props.timeFocus.id, + !this.props.timeFocus.keep + ); + }); + this.isInit = true; }; componentDidMount() { diff --git a/src/components/Paper/LineChart/LineChart.js b/src/components/Paper/LineChart/LineChart.js index 7569212..e8e2628 100644 --- a/src/components/Paper/LineChart/LineChart.js +++ b/src/components/Paper/LineChart/LineChart.js @@ -95,21 +95,6 @@ class LineChart extends Component { let counters = Object.assign({}, this.state.counters); - if( nextProps.box.values['chartType'] !== this.chartType){ - //- LINE FILL => LINE Change - if( nextProps.box.values['chartType'] === 'LINE' && this.chartType === 'LINE FILL' ){ - // this.removeLineFill(); - } - //- LINE or LINE FILL => STACK; - if( nextProps.box.values['chartType'] === 'STACK AREA' && ( this.chartType === 'LINE' || this.chartType === 'LINE FILL') ){ - // this.removeLineFill(); - // this.removeLine(); - } - // STACK => LINE or LINE FILL - if( (nextProps.box.values['chartType'] === 'LINE' || nextProps.box.values['chartType'] === 'LINE FILL' ) && ( this.chartType === 'STACK AREA') ){ - // this.removeStackFill(); - } - } if (nextProps.countersHistory && this.lastCountersHistoryTime !== nextProps.countersHistoryTimestamp) { @@ -120,12 +105,12 @@ class LineChart extends Component { counters = this.loadHistoryCounter(nextProps.countersHistory, counterKey, nextProps.longTerm); this.setState({ - counters: counters + counters: counters, }); } else { counters[counterKey] = []; this.setState({ - counters: counters + counters: counters, }); } } @@ -144,7 +129,9 @@ class LineChart extends Component { this.setState({ startTime: startTime, - endTime: endTime + endTime: endTime, + search : true, + options : {...this.graph,type : this.chartType}, }); } @@ -199,21 +186,67 @@ class LineChart extends Component { noData = false; } } - + //- object remove , couter remove + const removeCounter = this.removeCounter(this.props.box.option,counters); + removeCounter.forEach(d=> delete counters[d.counter]); this.setState({ startTime: startTime, endTime: endTime, - counters: counters, + counters: noData ? [] : counters, + removeCounter : removeCounter, + removeObject : this.removeObject(nextProps.objects,this.props.objects), noData: noData, - options : {...this.graph,type : this.chartType} + options : {...this.graph,type : nextProps.box.values['chartType']}, + search : false }); } - //- 이전 툴팁이 고정 되었으면 자동 해지 할수 있도록 이벤트 체크 - this.chartType = nextProps.box.values['chartType']; - if(!this.props.timeFocus.keep) { - // this.drawTimeFocus(); + } + removeObject(prevList, currentList){ + const ret = []; + let currentInstanceMap = {}; + if (currentList && currentList.length > 0) { + for (let i = 0; i < currentList.length; i++) { + currentInstanceMap[currentList[i].objHash] = true; + } } + + if (prevList && prevList.length > 0) { + for (let i = 0; i < prevList.length; i++) { + if (!currentInstanceMap[prevList[i].objHash]) { + + for (let counterKey in this.state.counters) { + + let thisOption = null; + for (let j = 0; j < this.props.box.option.length; j++) { + if (this.props.box.option[j].counterKey === counterKey) { + thisOption = this.props.box.option[j]; + break; + } + } + + if (thisOption) { + ret.push( { key : prevList[i].objHash , counter : counterKey}); + } + } + } + } + } + return ret; + } + removeCounter(boxOption,counters){ + const ret = []; + for(const counterKey in counters) { + let thisOption = boxOption.filter((d) => { + return d.counterKey === counterKey + })[0]; + if(!thisOption){ + ret.push(this.props.objects.map( d => { + return {key : d.objHash , counter : counterKey} + })); + } + } + return ret.flatMap(d=>d); } loadHistoryCounter(countersHistory, counterKey, longTerm) { @@ -323,6 +356,7 @@ class LineChart extends Component { this.graph.autoMaxY = maxY; + if (!this.graph.maxY) { this.graph.maxY = maxY * 1.2; } @@ -366,7 +400,7 @@ class LineChart extends Component { // } // }else{ // // this.graphResize(); - // // this.removeObjLine(prevProps.objects, this.props.objects); + // // this.removeObjLine(prevProps.objects, this.props.objects); // // } }; @@ -390,9 +424,15 @@ class LineChart extends Component { counters={this.state.counters} noData={this.state.noData} options={this.state.options} + removeCounter={this.state.removeCounter} + removeObject={this.state.removeObject} box = {this.props.box} setTitle={this.props.setTitle} - >\ + removeTitle={this.props.removeTitle} + search={this.state.search} + hideTooltip={this.props.hideTooltip} + showTooltip={this.props.showTooltip} + >
@@ -414,8 +454,6 @@ let mapStateToProps = (state) => { return { objects: state.target.objects, config: state.config, - filterMap: state.target.filterMap, - timeFocus: state.timeFocus, range: state.range, }; }; From 18af3abf7b5acee42ee716a94d45becb570943a2 Mon Sep 17 00:00:00 2001 From: "yeosong.heo" Date: Sat, 26 Oct 2019 15:51:48 +0900 Subject: [PATCH 62/77] :zap: line chart zoom-in adding --- src/components/Box/Box.js | 11 +- src/components/Paper/LineChart/Line.js | 144 +++++++++++++++---------- 2 files changed, 95 insertions(+), 60 deletions(-) diff --git a/src/components/Box/Box.js b/src/components/Box/Box.js index 6d9f912..3626342 100644 --- a/src/components/Box/Box.js +++ b/src/components/Box/Box.js @@ -64,12 +64,13 @@ class Box extends Component { this.props.objects.filter((d) => { return d.objFamily === familyName; }).forEach((d, idx, array) => { - icon += this.props.counterInfo.objTypesMap[d.objType].icon; - if (idx !== array.length - 1) { - icon += ","; + if(this.props.counterInfo.objTypesMap[d.objType]) { + icon += this.props.counterInfo.objTypesMap[d.objType].icon; + if (idx !== array.length - 1) { + icon += ","; + } + this.iconMap[this.props.counterInfo.objTypesMap[d.objType].icon] = true; } - - this.iconMap[this.props.counterInfo.objTypesMap[d.objType].icon] = true; }); titles[title] = { diff --git a/src/components/Paper/LineChart/Line.js b/src/components/Paper/LineChart/Line.js index fc480bd..22408ee 100644 --- a/src/components/Paper/LineChart/Line.js +++ b/src/components/Paper/LineChart/Line.js @@ -18,48 +18,65 @@ class Line extends Component { g : null, }; isInit = false; - constructor(props) { - super(props); - - } + isZoom = false; + zoomData = null; componentWillReceiveProps(nextProps){ if(!this.isInit){ return; } - +//- realtime <=> search reset if(nextProps.search !== this.props.search){ switch(this.props.options.type){ - case "LINE FILL": - case "LINE": - this.removePathLine(true); - break; case "STACK AREA": this.removePathLine(false); break; + default: + this.removePathLine(true); } + this.isZoom = false; + this.zoomData = null; } - +// box option change; const {type} = nextProps.options; const thisType = this.props.options.type; if( type !== thisType){ switch(type){ - case "LINE FILL": - case "LINE": - this.removePathLine(true); - break; case "STACK AREA": this.removePathLine(false); break; + default: + this.removePathLine(true); } + this.isZoom = false; + this.zoomData = null; + } + + if( nextProps.options !== this.props.options && !this.isZoom){ + this.changedOption(nextProps.options, nextProps); + } + if(!this.isZoom) { + this.paint(nextProps); + } + + const isResize = nextProps.options.width !== this.props.options.width || nextProps.options.height !== this.props.options.height; + + if(this.isZoom && this.zoomData && isResize) { + this.changedOption({...this.zoomData.options , + width : nextProps.options.width, + height : nextProps.options.height, + },this.zoomData); + this.paint(this.zoomData); } - if( nextProps.options !== this.props.options){ - this.changedOption(nextProps.options,nextProps); + if(nextProps.timeFocus.active && !nextProps.noData) { + this.drawTimeFocus(nextProps.timeFocus.keep, nextProps); + } + if(!nextProps.timeFocus.active && !nextProps.noData) { + this.removeFocus(nextProps); } - this.paint(nextProps); }; removePathLine(isStack){ if(!isStack) { @@ -87,6 +104,8 @@ class Line extends Component { } paint (data){ + + this.clearLine(); if (data.objects) { let instanceMetricCount = {}; @@ -122,7 +141,7 @@ class Line extends Component { } } this.moveTooltip(); - this.drawTimeFocus(data.timeFocus.keep,data); + }; @@ -173,9 +192,14 @@ class Line extends Component { this.props.removeTitle(counterKey); }; + removeFocus(nextProps){ + if(nextProps.timeFocus.id !== this.props.box.key) { + this.focus.select("line.focus-line").style('display','none'); + } + } drawTimeFocus=(isFixed=false,nextProps)=>{ - if( isFixed && !this.props.noData){ + if( isFixed ){ if( Object.keys(this.props.counters).map(k => this.props.counters[k][0] ? this.props.counters[k][0].time : null).filter( t => this.props.timeFocus.time > t ).length ) { let hoverLine = this.focus.selectAll("line.focus-line"); hoverLine.attr("x1", (d) => this.xScale(d)) @@ -193,14 +217,13 @@ class Line extends Component { .attr("x2", (d) => this.xScale(d)) .exit() .remove(); + hoverLine.style("display","block"); }else{ // 해제 this.props.setTimeFocus(false,null,null,false); } - }else if( !this.props.noData - && this.props.active - && this.props.timeFocus.id !== this.props.box.key) { + }else if( nextProps.timeFocus.id !== this.props.box.key) { let hoverLine = this.focus.selectAll("line.focus-line"); hoverLine.attr("x1", (d) =>this.xScale(d)) .attr("x2", (d) =>this.xScale(d)); @@ -217,8 +240,10 @@ class Line extends Component { .attr("x2", (d) =>this.xScale(d)) .exit() .remove(); + hoverLine.style("display","block"); + }else{ - this.focus.select("line.focus-line").remove(); + this.focus.select("line.focus-line").style("display","none"); } }; drawStackArea=(thisOption,counterKey,data) => { @@ -414,19 +439,17 @@ class Line extends Component { return realTime ? svg : svg.transition().duration(500); } changedOption(changed,props){ + + // if(changed.width !== this.props.options.width || changed.height !== this.props.options.height ) { this.brush.extent([[0, 0], [changed.width, changed.height]]); this.brushG.call(this.brush); this.area_clip .attr("width", changed.width) .attr("height", changed.height); - this.xScale = this.xScale.range([0, changed.width]); this.yScale = this.yScale.range([changed.height, 0]); - - this.xScale.domain([props.startTime,props.endTime]); - this.yScale.domain([0, changed.maxY]); - - + this.xScale.domain([props.startTime, props.endTime]); + this.yScale.domain([0, props.options.maxY]); let xAxisCount = Math.floor(changed.width / changed.xAxisWidth); if (xAxisCount < 1) { xAxisCount = 1; @@ -435,33 +458,51 @@ class Line extends Component { if (yAxisCount < 1) { yAxisCount = 1; } -// Y축 + // Y축 this.tickY.ticks(yAxisCount); this.gridTickY.tickSize(-changed.width) - .ticks(yAxisCount); + .ticks(yAxisCount); this.axisY.transition().duration(500).call(this.tickY); this.gridY.transition().duration(500).call(this.gridTickY); //- X축 this.tickX.tickFormat(d3.timeFormat(changed.timeFormat)) - .ticks(xAxisCount); + .ticks(xAxisCount); this.gridTickX.tickSize(-changed.height) - .ticks(xAxisCount); + .ticks(xAxisCount); this.axisX.attr("transform", `translate(0,${changed.height})`) - .call(this.tickX); + .call(this.tickX); this.gridX.attr("transform", `translate(0,${changed.height})`) - .call(this.gridTickX); - + .call(this.gridTickX); + this.focus.selectAll("line").attr("y2",changed.height); + // if(changed.height !== this.props.options.height || + // changed.width !== this.props.options.width ){ + // this.paint(this.props); + // } } + zoomBrush = () => { const extent = d3.event.selection; - const {realTime} = this.props.range; + if(extent) { + this.brushG.call(this.brush.move,null); + this.isZoom = true; + const endTime = this.xScale.invert(extent[1]); + const startTime = this.xScale.invert(extent[0]); + + this.zoomData = {...this.props,startTime : startTime.getTime(),endTime : endTime.getTime()} + + this.changedOption(this.zoomData.options,this.zoomData); + this.paint(this.zoomData); + }else{ + this.isZoom = false; + // restore + this.paint(this.props); + } + - // this.focus.selectAll("circle").style("display", "none"); - // this.focus.select("line.x-hover-line").style("display", "none"); }; mouseOverObject = (obj, thisOption, color) => { @@ -516,10 +557,6 @@ class Line extends Component { const {width,height,margin} = this.props.options; const {options} = this.props; - // const {svg}= this.state; - - // console.log('',svg,margin,width,height); - this.svg = d3.select(g.parentNode); this.area_clip = this.svg.append("defs") @@ -545,15 +582,10 @@ class Line extends Component { this.brush = d3.brushX() .extent([[0, 0], [width, height]]) - .on("start",()=>{ - // this.props.hideTooltip(); - this.focus.selectAll("circle").style("display", "none"); - this.focus.selectAll("line").style("display", "none"); - }) .on("end", this.zoomBrush); - this.brushG = this.top.append("g").attr("class", "brush") - .call(this.brush); + this.brushG = this.top.append("g").attr("class", "brush"); + this.brushG.call(this.brush); // // //Axis Draw this.xScale = d3.scaleTime().range([0, width]); @@ -662,8 +694,10 @@ class Line extends Component { this.props.hideTooltip(); this.currentTooltipTime = null; - // if(!this.props.timeFocus.keep) - // this.props.setTimeFocus(false, null, this.props.box.key); + //- 해제 + if(!this.props.timeFocus.keep) { + this.props.setTimeFocus(false, null, this.props.box.key); + } }); this.bisector = d3.bisector(function (d) { @@ -763,9 +797,9 @@ class Line extends Component { tooltip.counterSum = numeral(that.counterSum).format(that.props.config.numberFormat); that.currentTooltipTime = tooltip.timeValue; - // if(!that.props.timeFocus.keep){ - // that.props.setTimeFocus(true,x0.getTime(),that.props.box.key); - // } + if(!that.props.timeFocus.keep){ + that.props.setTimeFocus(true,x0.getTime(),that.props.box.key); + } that.props.showTooltip(xPos, yPos, that.props.options.margin.left, that.props.options.margin.top, tooltip); From 0e390813fb3515fcd11dce9deb662ea18f857087 Mon Sep 17 00:00:00 2001 From: "yeosong.heo" Date: Sat, 26 Oct 2019 16:14:53 +0900 Subject: [PATCH 63/77] :zap: line chart timeformat tick sec --- src/components/Paper/LineChart/Line.js | 29 ++++++++++++++++----- src/components/Paper/LineChart/LineChart.js | 9 ------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/components/Paper/LineChart/Line.js b/src/components/Paper/LineChart/Line.js index 22408ee..9588b0c 100644 --- a/src/components/Paper/LineChart/Line.js +++ b/src/components/Paper/LineChart/Line.js @@ -22,6 +22,10 @@ class Line extends Component { isZoom = false; zoomData = null; + zoomReset(){ + this.isZoom = false; + this.zoomData = null; + } componentWillReceiveProps(nextProps){ if(!this.isInit){ return; @@ -35,8 +39,10 @@ class Line extends Component { default: this.removePathLine(true); } - this.isZoom = false; - this.zoomData = null; + this.zoomReset(); + } + if(nextProps.search){ + this.zoomReset(); } // box option change; const {type} = nextProps.options; @@ -50,8 +56,7 @@ class Line extends Component { default: this.removePathLine(true); } - this.isZoom = false; - this.zoomData = null; + this.zoomReset(); } if( nextProps.options !== this.props.options && !this.isZoom){ @@ -194,7 +199,7 @@ class Line extends Component { removeFocus(nextProps){ if(nextProps.timeFocus.id !== this.props.box.key) { - this.focus.select("line.focus-line").style('display','none'); + this.focus.select("line.focus-line").remove(); } } drawTimeFocus=(isFixed=false,nextProps)=>{ @@ -243,7 +248,7 @@ class Line extends Component { hoverLine.style("display","block"); }else{ - this.focus.select("line.focus-line").style("display","none"); + this.focus.select("line.focus-line").remove(); } }; drawStackArea=(thisOption,counterKey,data) => { @@ -438,6 +443,15 @@ class Line extends Component { const {realTime} = this.props.range; return realTime ? svg : svg.transition().duration(500); } + getTimeFormat(duration){ + + if( duration >= (60000 * 5)) { + return this.props.config.minuteFormat; + }else{ + return this.props.config.timeFormat; + } + } + changedOption(changed,props){ // if(changed.width !== this.props.options.width || changed.height !== this.props.options.height ) { @@ -467,7 +481,7 @@ class Line extends Component { this.axisY.transition().duration(500).call(this.tickY); this.gridY.transition().duration(500).call(this.gridTickY); //- X축 - this.tickX.tickFormat(d3.timeFormat(changed.timeFormat)) + this.tickX.tickFormat(d3.timeFormat(this.getTimeFormat(props.endTime - props.startTime))) .ticks(xAxisCount); this.gridTickX.tickSize(-changed.height) @@ -499,6 +513,7 @@ class Line extends Component { }else{ this.isZoom = false; // restore + this.changedOption(this.props.options,this.props); this.paint(this.props); } diff --git a/src/components/Paper/LineChart/LineChart.js b/src/components/Paper/LineChart/LineChart.js index e8e2628..04abe54 100644 --- a/src/components/Paper/LineChart/LineChart.js +++ b/src/components/Paper/LineChart/LineChart.js @@ -18,28 +18,19 @@ class LineChart extends Component { lastCountersHistoryTime = null; historyInit = {}; chartType = "LINE"; - timeFocusId = null; graph = { margin: { top: 15, right: 15, bottom: 25, left: 40 }, - svg: null, - overlay: null, width: 700, height: 50, - x: null, - y: null, - path: null, startTime: (new ServerDate()).getTime() - (1000 * 60 * 10), endTime: (new ServerDate()).getTime(), timeFormat: "%H:%M", - timeFormatSec: "%H:%M:%S", fullTimeFormat: "%Y-%m-%d %H:%M:%S", xAxisWidth: 70, yAxisHeight: 30, noData: true, - bisector: null, - currentTooltipTime: null, opacity : 0.3 }; From e74f7a4d6a7cff3914c7c50e604eab370bc172da Mon Sep 17 00:00:00 2001 From: "yeosong.heo" Date: Sat, 26 Oct 2019 16:33:13 +0900 Subject: [PATCH 64/77] =?UTF-8?q?:fire:=20linechart=20=EA=B8=B0=EC=A1=B4?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84=20=ED=8C=8C=EC=9D=BC=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Paper/LineChart/LineChart-BackUP.js | 1259 ----------------- 1 file changed, 1259 deletions(-) delete mode 100644 src/components/Paper/LineChart/LineChart-BackUP.js diff --git a/src/components/Paper/LineChart/LineChart-BackUP.js b/src/components/Paper/LineChart/LineChart-BackUP.js deleted file mode 100644 index 56e0442..0000000 --- a/src/components/Paper/LineChart/LineChart-BackUP.js +++ /dev/null @@ -1,1259 +0,0 @@ -import React, {Component} from 'react'; -import './LineChart.css'; -import {connect} from 'react-redux'; -import {withRouter} from 'react-router-dom'; -import * as d3 from "d3"; -import * as _ from "lodash"; -import ServerDate from "../../../common/ServerDate"; -import InstanceColor from "../../../common/InstanceColor"; -import numeral from "numeral"; -import { - setRangeDateHoursMinutes, - setRealTimeRangeStepValue, - setRealTimeValue, - setSearchCondition, - setTimeFocus -} from "../../../actions"; -import moment from 'moment'; - -class LineChart extends Component { - - lastCountersTime = null; - lastCountersHistoryTime = null; - historyInit = {}; - chartType = "LINE"; - timeFocusId = null; - graph = { - margin: { - top: 15, right: 15, bottom: 25, left: 40 - }, - svg: null, - overlay: null, - width: null, - height: null, - x: null, - y: null, - path: null, - startTime: (new ServerDate()).getTime() - (1000 * 60 * 10), - endTime: (new ServerDate()).getTime(), - timeFormat: "%H:%M", - timeFormatSec: "%H:%M:%S", - fullTimeFormat: "%Y-%m-%d %H:%M:%S", - xAxisWidth: 70, - yAxisHeight: 30, - noData: true, - bisector: null, - currentTooltipTime: null, - opacity : 0.3 - }; - - constructor(props) { - super(props); - this.state = { - counters: {}, - maxY: null, - autoMaxY: null - } - } - - componentDidMount() { - if (this.props.config.colorType === "white") { - this.graph.opacity = 0.3; - } else { - this.graph.opacity = 0.6; - } - - - this.graph.timeFormat = this.props.config.minuteFormat; - this.graph.fullTimeFormat = this.props.config.dateFormat + " " + this.props.config.timeFormat; - this.graphInit(); - } - - shouldComponentUpdate(nextProps, nextState) { - - /*if (this.props.visible && this.props.countersHistoryTimestamp !== nextProps.countersHistoryTimestamp) { - return true; - }*/ - - return !!this.props.visible; - } - - - - componentWillReceiveProps(nextProps) { - let counters = Object.assign({}, this.state.counters); - - - if( nextProps.box.values['chartType'] !== this.chartType){ - //- LINE FILL => LINE Change - if( nextProps.box.values['chartType'] === 'LINE' && this.chartType === 'LINE FILL' ){ - this.removeLineFill(); - } - //- LINE or LINE FILL => STACK; - if( nextProps.box.values['chartType'] === 'STACK AREA' && ( this.chartType === 'LINE' || this.chartType === 'LINE FILL') ){ - this.removeLineFill(); - this.removeLine(); - } - // STACK => LINE or LINE FILL - if( (nextProps.box.values['chartType'] === 'LINE' || nextProps.box.values['chartType'] === 'LINE FILL' ) && ( this.chartType === 'STACK AREA') ){ - this.removeStackFill(); - } - } - - if (nextProps.countersHistory && this.lastCountersHistoryTime !== nextProps.countersHistoryTimestamp) { - - this.lastCountersHistoryTime = nextProps.countersHistoryTimestamp; - for (let i = 0; i < nextProps.box.option.length; i++) { - let counterKey = nextProps.box.option[i].counterKey; - if (nextProps.countersHistory[counterKey]) { - - counters = this.loadHistoryCounter(nextProps.countersHistory, counterKey, nextProps.longTerm); - this.setState({ - counters: counters - }); - } else { - counters[counterKey] = []; - this.setState({ - counters: counters - }); - } - } - - this.graph.maxY = 1; - - this.setMaxY(counters, nextProps.box.option); - - let startTime = (new ServerDate()).getTime() - (1000 * 60 * 10); - let endTime = (new ServerDate()).getTime(); - - if (nextProps.countersHistoryFrom && nextProps.countersHistoryTo) { - endTime = nextProps.countersHistoryTo; - startTime = nextProps.countersHistoryFrom; - } - - this.setState({ - startTime: startTime, - endTime: endTime - }); - } - - if (nextProps.counters && nextProps.time !== this.lastCountersTime) { - - this.lastCountersTime = nextProps.time; - - let endTime = nextProps.time; - let startTime = nextProps.time - (1000 * 60 * 10); //- realtime - - for (let i = 0; i < nextProps.box.option.length; i++) { - let counterKey = nextProps.box.option[i].counterKey; - counters[counterKey] = counters[counterKey] || []; - counters[counterKey].push({ - time: nextProps.time, - data: nextProps.counters[counterKey] - }); - } - - for (let counterKey in counters) { - let overIndex = -1; - for (let i = 0; i < counters[counterKey].length; i++) { - if (counters[counterKey][i].time < startTime) { - overIndex = i; - } else { - break; - } - } - - if (overIndex > -1) { - counters[counterKey].splice(0, overIndex + 1); - } - } - - this.setMaxY(counters, nextProps.box.option); - - let noData = true; - if (Array.isArray(nextProps.box.option)) { - for (let i = 0; i < nextProps.box.option.length; i++) { - let counterKey = nextProps.box.option[i].counterKey; - if (nextProps.counters[counterKey]) { - if (Object.keys(nextProps.counters[counterKey]).length !== 0) { - noData = false; - break; - } - } - - } - } else { - let counterKey = nextProps.box.option.counterKey; - if (Object.keys(nextProps.counters[counterKey]).length !== 0) { - noData = false; - } - } - - this.setState({ - startTime: startTime, - endTime: endTime, - counters: counters, - noData: noData - }); - - } - //- 이전 툴팁이 고정 되었으면 자동 해지 할수 있도록 이벤트 체크 - this.chartType = nextProps.box.values['chartType']; - if(!this.props.timeFocus.keep) { - this.drawTimeFocus(); - } - } - - loadHistoryCounter(countersHistory, counterKey, longTerm) { - let decimalPoint = this.props.config.decimalPoint; - let pow = 1; - - if (decimalPoint > 0) { - pow = Math.pow(10, decimalPoint); - } - - let counters = this.state.counters; - counters[counterKey] = []; - const timeKeyRow = {}; - for (let objHash in countersHistory[counterKey]) { - - let timeList = countersHistory[counterKey][objHash].timeList; - let valueList = countersHistory[counterKey][objHash].valueList; - let unit = countersHistory[counterKey][objHash].unit; - - for (let j = 0; j < timeList.length; j++) { - let row = {}; - let timeUnit = 2000; - if (longTerm) { - timeUnit = 1000 * 60 * 5; - } - row.time = parseInt(timeList[j] / timeUnit, 10) * timeUnit; - row.data = {}; - row.data[objHash] = { - objHash: objHash, - value: Math.round(valueList[j] * pow) / pow, - unit: unit - }; - - if (!timeKeyRow[row.time]) { - timeKeyRow[row.time] = row; - } else { - timeKeyRow[row.time].data[objHash] = { - objHash: objHash, - value: Math.round(valueList[j] * pow) / pow, - unit: unit - }; - } - } - } - - for (const timeKey in timeKeyRow) { - counters[counterKey].push(timeKeyRow[timeKey]); - } - counters[counterKey].sort((a, b) => a.time - b.time); - - return counters; - } - - setMaxY = (counters, option) => { - - - let metricMap = {}; - option.forEach((option) => { - metricMap[option.counterKey] = true; - }); - - let maxY = 0; - if( this.chartType === "STACK AREA"){ - for (let attr in counters) { - if (!metricMap[attr]) { - continue; - } - for (let i = 0; i < counters[attr].length; i++) { - // y축 sum - const sum = Object.keys(counters[attr][i].data) - .map(_key => Number(counters[attr][i].data[_key].value)) - .reduce((acc,cur)=> acc+cur,0); - if( sum > maxY){ - maxY = sum; - } - } - } - - }else { - for (let attr in counters) { - if (!metricMap[attr]) { - continue; - } - - for (let i = 0; i < counters[attr].length; i++) { - for (let hash in counters[attr][i].data) { - if (Array.isArray(counters[attr][i].data[hash].value)) { - for (let j = 0; j < counters[attr][i].data[hash].value.length; j++) { - // line chart - if (Number(counters[attr][i].data[hash].value[j]) > maxY) { - maxY = Number(counters[attr][i].data[hash].value[j]); - } - } - } else { - if (Number(counters[attr][i].data[hash].value) > maxY) { - maxY = Number(counters[attr][i].data[hash].value); - } - } - } - } - } - } - - if (!maxY || maxY < 1) { - maxY = 1; - } - - this.graph.autoMaxY = maxY; - - if (!this.graph.maxY) { - this.graph.maxY = maxY * 1.2; - } - - if (this.graph.autoMaxY > this.graph.maxY) { - this.graph.maxY = this.graph.autoMaxY * 1.2; - } - - - }; - - removeObjLine(prevList, currentList) { - // 제외된 인스턴스 찾기 - let currentInstanceMap = {}; - if (currentList && currentList.length > 0) { - for (let i = 0; i < currentList.length; i++) { - currentInstanceMap[currentList[i].objHash] = true; - } - } - - if (prevList && prevList.length > 0) { - for (let i = 0; i < prevList.length; i++) { - if (!currentInstanceMap[prevList[i].objHash]) { - - for (let counterKey in this.state.counters) { - - let thisOption = null; - for (let j = 0; j < this.props.box.option.length; j++) { - if (this.props.box.option[j].counterKey === counterKey) { - thisOption = this.props.box.option[j]; - break; - } - } - - if (thisOption) { - this.removeObjectLineOnly(prevList[i], counterKey); - } - } - } - } - } - } - - componentDidUpdate = (prevProps, prevState) => { - if (prevProps.layoutChangeTime !== this.props.layoutChangeTime) { - if (this.graphResize()) { - this.draw(); - } - } else { - this.graphResize(); - this.draw(); - this.removeObjLine(prevProps.objects, this.props.objects); - } - }; - - - - moveTooltip = () => { - if (this.graph.currentTooltipTime) { - let xPosition = this.graph.x(this.graph.currentTooltipTime); - this.graph.focus.selectAll("circle").attr("cx", xPosition); - - let hoverLine = this.graph.focus.select("line.x-hover-line"); - hoverLine.attr("x1", xPosition); - hoverLine.attr("x2", xPosition); - } - }; - - leftAxisFormat = (d) => { - return numeral(d).format('0.0a'); - }; - setAnimation(svg){ - const {realTime} = this.props.range; - return realTime ? svg : svg.transition().duration(500); - } - getTimeFormat(){ - if( this.state.endTime-this.state.startTime < (60000 * 5)){ - return this.graph.timeFormatSec; - } else{ - return this.graph.timeFormat; - } - } - graphAxis = (width, height, init) => { - this.graph.x = d3.scaleTime().range([0, width]); - this.graph.y = d3.scaleLinear().range([height, 0]); - - this.graph.x.domain([this.state.startTime, this.state.endTime]); - this.graph.y.domain([0, this.graph.maxY]); - - let xAxisCount = Math.floor(this.graph.width / this.graph.xAxisWidth); - if (xAxisCount < 1) { - xAxisCount = 1; - } - - let yAxisCount = Math.floor(this.graph.height / this.graph.yAxisHeight); - if (yAxisCount < 1) { - yAxisCount = 1; - } - - //- y - if (init) { - this.graph.svg.insert("g", ":first-child").attr("class", "axis-y").call(d3.axisLeft(this.graph.y).tickFormat(this.leftAxisFormat).ticks(yAxisCount)); - this.graph.svg.insert("g", ":first-child").attr("class", "grid-y").style("stroke-dasharray", "5 2").style("opacity", this.graph.opacity).call(d3.axisLeft(this.graph.y).tickSize(-this.graph.width).tickFormat("").ticks(yAxisCount)); - } else { - this.setAnimation(this.graph.svg.select(".axis-x")) - .call(d3.axisBottom(this.graph.x).tickFormat(d3.timeFormat(this.getTimeFormat())).ticks(xAxisCount)); - this.setAnimation(this.graph.svg.select(".grid-x")) - .call(d3.axisBottom(this.graph.x).tickSize(-this.graph.height).tickFormat("").ticks(xAxisCount)); - } - - if (init) { - this.graph.svg.insert("g", ":first-child").attr("class", "axis-x").attr("transform", "translate(0," + this.graph.height + ")").call(d3.axisBottom(this.graph.x).tickFormat(d3.timeFormat(this.graph.timeFormat)).ticks(xAxisCount)); - this.graph.svg.insert("g", ":first-child").attr("class", "grid-x").style("stroke-dasharray", "5 2").style("opacity", this.graph.opacity).attr("transform", "translate(0," + this.graph.height + ")").call(d3.axisBottom(this.graph.x).tickSize(-this.graph.height).tickFormat("").ticks(xAxisCount)); - - } else { - this.graph.svg.select(".axis-y").transition().duration(500).call(d3.axisLeft(this.graph.y).tickFormat(this.leftAxisFormat).ticks(yAxisCount)); - this.graph.svg.select(".grid-y").transition().duration(500).call(d3.axisLeft(this.graph.y).tickSize(-this.graph.width).tickFormat("").ticks(yAxisCount)); - } - - - }; - - manualTooltipHide = ( ) =>{ - - let layer = this.refs.lineChartRoot.parentNode.parentNode.parentNode.parentNode.parentNode; - layer.style.zIndex = 5; - this.graph.focus.selectAll("circle").style("display", "none"); - - let hoverLine = this.graph.focus.select("line.x-hover-line"); - if (hoverLine.size() > 0) { - hoverLine.style("display", "none"); - } - this.props.hideTooltip(); - }; - - - replaceAll(str, searchStr, replaceStr) { - return str.split(searchStr).join(replaceStr); - } - - replaceName (name) { - if (name) { - return this.replaceAll(this.replaceAll(name, "%", "_PCT_"), '$', '_DOLLAR_'); - } - return name; - } - removeStackFill = () =>{ - this.graph.svg.select('g.stack-area').selectAll('path.line') - .transition() - .delay(100) - .remove(); - }; - - removeLineFill = () =>{ - for(const obj of this.props.objects) { - for (let counterKey in this.state.counters) { - let areaClass = "area-" + obj.objHash + "-" + this.replaceName(counterKey); - this.graph.line.selectAll("path." + areaClass) - .transition() - .delay(100) - .remove(); - } - } - }; - removeLine = () =>{ - for(const obj of this.props.objects) { - for (let counterKey in this.state.counters) { - let lineClass = "line-" + obj.objHash + "-" + this.replaceName(counterKey); - this.graph.line.selectAll("path." + lineClass) - .transition() - .delay(100) - .remove(); - } - } - }; - - - removeObjectLine = (obj, counterKey) => { - let pathClass = "line-" + obj.objHash + "-" + this.replaceName(counterKey); - let path = this.graph.line.selectAll("path." + pathClass); - - // 데이터 삭제 - let counters = Object.assign({}, this.state.counters); - if (counters[counterKey]) { - delete counters[counterKey]; - } - delete this.state.counters[counterKey]; - this.setState({ - counters: counters - }); - - // 라인 그래프 삭제 - if (path && path.size() > 0) { - path.remove(); - } - - // 툴팁 그래프 삭제 - let circleKey = "circle-" + obj.objHash + "_" + this.replaceName(counterKey); - let circle = this.graph.focus.selectAll("circle." + circleKey); - - if (circle.size() > 0) { - circle.remove(); - } - - // 제목 삭제 - this.props.removeTitle(counterKey); - }; - - removeObjectLineOnly = (obj, counterKey) => { - let pathClass = "line-" + obj.objHash + "-" + this.replaceName(counterKey); - let path = this.graph.line.selectAll("path." + pathClass); - - // 라인 그래프 삭제 - if (path && path.size() > 0) { - path.remove(); - } - - // 툴팁 그래프 삭제 - let circleKey = "circle-" + obj.objHash + "_" + this.replaceName(counterKey); - let circle = this.graph.focus.selectAll("circle." + circleKey); - - if (circle.size() > 0) { - circle.remove(); - } - - }; - - drawStackArea=(thisOption,counterKey) => { - - let instanceMetricCount = {}; - const color = {}; - //- instance color making - for (const attr in this.props.objects) { - const _obj = this.props.objects[attr]; - if (_obj.objFamily === thisOption.familyName) { - if (!instanceMetricCount[_obj.objHash]) { - instanceMetricCount[_obj.objHash] = 0; - } - if (this.props.config.graph.color === "metric") { - const _cl = InstanceColor.getMetricColor(thisOption.counterKey, this.props.config.colorType); - color[_obj.objHash] = _cl; - } else { - const _cl = InstanceColor.getInstanceColors(this.props.config.colorType)[_obj.objHash][(instanceMetricCount[_obj.objHash]++) % 5]; - color[_obj.objHash] = _cl; - } - } - } - //- instance data flat data making - const stackData = _(this.state.counters[counterKey]) - .map((d) => { - const _r = Object.keys(d.data).map(key => { - const _keyValue = []; - _keyValue['objHash'] = d.data[key].objHash; - _keyValue['time'] = d.time; - _keyValue['value'] = Number(d.data[key].value); - _keyValue['color'] = color[d.data[key].objHash]; - return _keyValue; - }); - return _r; - }).flatMapDepth().value(); - //- 인스턴스 별 데이터로 변환 - let ld = d3.nest().key(d => d.objHash).entries(stackData); - const _sort = []; - - //- 인스턴스 순서 정렬 - for (const attr in this.props.objects) { - const _obj = this.props.objects[attr]; - const _find = _.findIndex(ld, (o) => o.key === _obj.objHash); - if(_find > -1 ){ - _sort.push(ld[_find]); - } - - } - //- 인스턴스 그리기 - const area = d3.area().curve(d3[this.props.config.graph.curve]) - .x(d =>{ - return this.graph.x(d[0]); - }) - .y0(d => this.graph.y(d[2])) - .y1(d => this.graph.y(d[1])); - - if (this.props.config.graph.break === "Y") { - area.defined((d)=>{ - return !isNaN(d[0]) && !isNaN(d[1]) && !isNaN(d[2]); - }) - } - - //- 시간 별 Y축 데이터 어그리게이션 - let pre = {}; - //- 차트 갱신 - let paintGroup = this.graph.area.selectAll("path.line") - .data(_sort) - .attr("d",(d)=> { - const _d = _.map(d.values,(_node) =>{ - const _key = _node.time; - const pre_v = pre[_key] ? pre[_key] : 0; - const next_v = pre_v + Number(_node.value); - pre[_key] = next_v; - return [_node.time,next_v,pre_v]; - }); - return area(_d); - }); - - //- 차트 생성 - paintGroup.enter() - .append('path') - .attr("d",(d)=> { - const _d = _.map(d.values,(_node) =>{ - const _key = _node.time; - const pre_v = pre[_key] ? pre[_key] : 0; - const next_v = pre_v + _node.value; - pre[_key] = next_v; - return [_node.time,next_v,pre_v]; - }); - return area(_d); - }) - .attr('class',(d)=> `line ${d.key}` ) - .attr('data-col-name', (d)=> d.key) - .style("fill", (d)=> { - return color[d.key]; - }) - .attr("fill-opacity", this.props.config.graph.fillOpacity) - .attr("stroke",(d) =>{ - return color[d.key]; - }) - .style("stroke-width", this.props.config.graph.width) - .style("opacity", this.props.config.graph.opacity); - - //- 차트 갱신 후 데이터 삭제 - paintGroup.exit().remove(); - }; - - - drawObjectLine = (obj, option, counterKey, color) => { - const that = this; - if (this.props.box.values['chartType'] === "LINE FILL") { - - let areaClass = "area-" + obj.objHash + "-" + this.replaceName(counterKey); - let area = this.graph.line.selectAll("path." + areaClass) - - - if (area.size() < 1) { - area = this.graph.line.insert("path", ":first-child").attr("class", areaClass).style("stroke", color); - } - - let valueArea = d3.area().curve(d3[this.props.config.graph.curve]) - .x(function (d) { - return that.graph.x(d.time); - }) - .y0(that.graph.height) - .y1(function (counter) { - let objData = counter.data[obj.objHash]; - if (objData) { - return that.graph.y(objData.value); - } else { - return that.graph.y(0); - } - }); - - - if (this.props.config.graph.break === "Y") { - valueArea.defined(function (d) { - let objData = d.data ? d.data[obj.objHash] : null; - return objData && !isNaN(d.time) && !isNaN(objData.value) && !isNaN(that.graph.y(objData.value)); - }) - } - - - if (!this.props.filterMap[obj.objHash]) { - area.data([that.state.counters[counterKey]]) - .attr("d", valueArea) - .style("fill", color) - .style("opacity", 0) - .transition() - .delay(100); - } else { - area.data([that.state.counters[counterKey]]) - .attr("d", valueArea) - .style("fill", color) - .style("opacity", this.props.config.graph.fillOpacity) - .transition() - .delay(100); - } - } - - - - let pathClass = "line-" + obj.objHash + "-" + this.replaceName(counterKey); - let path = this.graph.line.selectAll("path." + pathClass); - - if (path.size() < 1) { - path = this.graph.line.insert("path", ":first-child").attr("class", pathClass).style("stroke", color); - if (this.props.config.graph.color === "instance") { - if (this.props.config.colorType === "white") { - this.props.setTitle(counterKey, option.title, "#333", option.familyName); - } else { - this.props.setTitle(counterKey, option.title, "white", option.familyName); - } - } else { - this.props.setTitle(counterKey, option.title, color, option.familyName); - } - } else { - path.style("stroke", color); - if (this.props.config.graph.color === "instance") { - if (this.props.config.colorType === "white") { - this.props.setTitle(counterKey, option.title, "#333", option.familyName); - } else { - this.props.setTitle(counterKey, option.title, "white", option.familyName); - } - } else { - this.props.setTitle(counterKey, option.title, color, option.familyName); - } - - let circleKey = "circle-" + obj.objHash + "_" + this.replaceName(counterKey); - that.graph.focus.select("circle." + circleKey).attr("stroke", color); - } - - let valueLine = d3.line().curve(d3[this.props.config.graph.curve]); - - if (this.props.config.graph.break === "Y") { - valueLine.defined(function (d) { - let objData = d.data ? d.data[obj.objHash] : null; - return objData && !isNaN(d.time) && !isNaN(objData.value) && !isNaN(that.graph.y(objData.value)); - }) - } - - valueLine.x(function (d) { - return that.graph.x(d.time); - }).y(function (counter) { - let objData = counter.data[obj.objHash]; - if (objData) { - return that.graph.y(objData.value); - } else { - return that.graph.y(0); - } - }); - - if (!this.props.filterMap[obj.objHash]) { - this.setAnimation(path.data([that.state.counters[counterKey]])).attr("d", valueLine).style("stroke-width", this.props.config.graph.width).style("opacity", 0); - } else { - this.setAnimation(path.data([that.state.counters[counterKey]])).attr("d", valueLine).style("stroke-width", this.props.config.graph.width).style("opacity", this.props.config.graph.opacity); - } - - }; - - drawTimer = null; - - draw = () => { - - let that = this; - if (this.drawTimer) { - clearTimeout(this.drawTimer); - this.drawTimer = null; - } - - this.drawTimer = setTimeout(() => { - if (this.refs.lineChart && this.graph.svg) { - - this.graphAxis(this.graph.width, this.graph.height, false); - if (this.props.objects) { - let instanceMetricCount = {}; - for (let counterKey in this.state.counters) { - let thisOption = that.props.box.option.filter((d) => {return d.counterKey === counterKey})[0]; - if (!thisOption) { - for (let i = 0; i < this.props.objects.length; i++) { - this.removeObjectLine(this.props.objects[i], counterKey); - } - } else if(this.chartType ==='STACK AREA') { - this.drawStackArea(thisOption,counterKey); - } else { - for (let i = 0; i < this.props.objects.length; i++) { - const obj = that.props.objects[i]; - - if (obj.objFamily === thisOption.familyName) { - if (!instanceMetricCount[obj.objHash]) { - instanceMetricCount[obj.objHash] = 0; - } - let color; - if (this.props.config.graph.color === "metric") { - color = InstanceColor.getMetricColor(thisOption.counterKey, this.props.config.colorType); - } else { - color = InstanceColor.getInstanceColors(this.props.config.colorType)[obj.objHash][(instanceMetricCount[obj.objHash]++) % 5]; - } - this.drawObjectLine(obj, thisOption, counterKey, color); - } - } - - } - } - } - } - - this.moveTooltip(); - if(this.props.timeFocus.keep) { - this.drawTimeFocus(true); - - } - }, 200); - - }; - - - drawTimeFocus=(isFixed=false)=>{ - - if( isFixed && !this.state.noData){ - if( Object.keys(this.state.counters).map(k => this.state.counters[k][0] ? this.state.counters[k][0].time : null).filter( t => this.props.timeFocus.time > t ).length ) { - let hoverLine = this.graph.focus.selectAll("line.focus-line"); - hoverLine.attr("x1", (d) => this.graph.x(d)) - .attr("x2", (d) => this.graph.x(d)); - - hoverLine.data([this.props.timeFocus.time]) - .enter() - .append("line") - .attr("class", "focus-line focus-hover-line") - .attr("y1", 0) - .attr("y2", this.graph.height) - .attr("x1", (d) => { - return this.graph.x(d); - }) - .attr("x2", (d) => this.graph.x(d)) - .exit() - .remove(); - }else{ - // 해제 - this.props.setTimeFocus(false,null,null,false); - } - - }else if( !this.state.noData - && this.props.timeFocus.active - && this.props.timeFocus.id !== this.props.box.key) { - let hoverLine = this.graph.focus.selectAll("line.focus-line"); - hoverLine.attr("x1", (d) =>this.graph.x(d)) - .attr("x2", (d) =>this.graph.x(d)); - - hoverLine.data([this.props.timeFocus.time]) - .enter() - .append("line") - .attr("class", "focus-line focus-hover-line") - .attr("y1", 0) - .attr("y2", this.graph.height) - .attr("x1", (d) =>{ - return this.graph.x(d); - }) - .attr("x2", (d) =>this.graph.x(d)) - .exit() - .remove(); - }else{ - this.graph.focus.select("line.focus-line").remove(); - } - }; - zoomBrush = () =>{ - const extent=d3.event.selection; - const {realTime} = this.props.range; - if(extent) { - this.graph.svg.select(".brush").call(this.brush.move, null); - const endTime = this.graph.x.invert(extent[1]); - const startTime = this.graph.x.invert(extent[0]); - if(realTime) { - //- 조회 - const startDate= moment(startTime); - this.props.setRealTimeRangeStepValue(false, this.props.range.longTerm, - this.props.range.value, - this.props.config.range.shortHistoryRange, this.props.config.range.shortHistoryStep); - this.props.setRangeDateHoursMinutes(startDate, startDate.hours(), startDate.minutes()); - this.props.setSearchCondition(startTime.valueOf(), endTime.valueOf(), new Date().getTime()); - - }else{ - //- 확대 - this.setState({ - startTime : startTime.getTime(), - endTime : endTime.getTime() - }); - } - }else{ - const {time,countersHistoryTo,countersHistoryFrom} = this.props; - if(realTime){ - this.setState({ - startTime : time - (1000*60*10), - endTime : time - }); - }else{ - this.setState({ - startTime : countersHistoryFrom, - endTime : countersHistoryTo - }); - } - - } - - }; - - - graphResize = () => { - let resized = false; - let box = this.refs.lineChart.parentNode.parentNode.parentNode.parentNode; - if ((box.offsetWidth - this.graph.margin.left - this.graph.margin.right !== this.graph.width) || (this.graph.height !== box.offsetHeight - this.graph.margin.top - this.graph.margin.bottom - 27)) { - resized = true; - this.graphInit(); - this.manualTooltipHide(); - } - - return resized; - }; - - mouseOverObject = (obj, thisOption, color) => { - let that = this; - let r = 3; - - let circleKey = "circle-" + obj.objHash + "_" + this.replaceName(thisOption.counterKey); - let circle = that.graph.focus.select("circle." + circleKey); - if (circle.size() < 1) { - circle = that.graph.focus.append("circle").attr("class", circleKey).attr("r", r).attr("stroke", color); - } - - if (this.props.filterMap[obj.objHash]) { - circle.style("opacity", 1); - } else { - circle.style("opacity", 0); - } - - }; - - mouseMoveObject = (obj, thisOption, counterKey, dataIndex, color, tooltip) => { - let that = this; - - let circleKey = "circle-" + obj.objHash + "_" + this.replaceName(thisOption.counterKey); - let unit = that.state.counters[counterKey][dataIndex].data[obj.objHash] ? that.state.counters[counterKey][dataIndex].data[obj.objHash].unit : ""; - - let valueOutput = obj.objHash && that.state.counters[counterKey][dataIndex].data[obj.objHash] ? that.state.counters[counterKey][dataIndex].data[obj.objHash].value : null ; - const valueOrigin = obj.objHash && that.state.counters[counterKey][dataIndex].data[obj.objHash] ? that.state.counters[counterKey][dataIndex].data[obj.objHash].value : null ; - if( this.chartType === "STACK AREA" && valueOutput ){ - valueOutput = this.counterSum + valueOutput; - this.counterSum = valueOutput; - } - - if (that.state.counters[counterKey][dataIndex].time) { - if (this.props.filterMap[obj.objHash]) { - tooltip.lines.push({ - instanceName: obj.objName, - circleKey: circleKey, - metricName: thisOption.title, - value: valueOutput ? valueOutput : null, - displayValue: valueOrigin ? numeral(valueOrigin).format(this.props.config.numberFormat) + " " + unit : null, - color: color - }); - } - } else { - that.graph.focus.select("circle." + circleKey).style("display", "none"); - } - - return true; - }; - - graphInit = () => { - - let that = this; - let box = this.refs.lineChart.parentNode.parentNode.parentNode.parentNode; - this.graph.width = box.offsetWidth - this.graph.margin.left - this.graph.margin.right; - this.graph.height = box.offsetHeight - this.graph.margin.top - this.graph.margin.bottom - 27; - - let svg = d3.select(this.refs.lineChart).select("svg"); - if (svg.size() > 0) { - svg.remove(); - } - - this.graph.svg = d3.select(this.refs.lineChart) - .append("svg") - .attr("width", this.graph.width + this.graph.margin.left + this.graph.margin.right) - .attr("height", this.graph.height + this.graph.margin.top + this.graph.margin.bottom); - - d3.select(this.refs.lineChart).select("svg").append("defs") - .append("svg:clipPath") - .attr("id", `area-clip${this.props.box.key}`) - .append("svg:rect") - .attr("x", 0) - .attr("y", 0) - .attr("width", this.graph.width) - .attr("height", this.graph.height); - - this.graph.svg = this.graph.svg.append("g").attr("class", "top-group") - .attr("transform", "translate(" + this.graph.margin.left + "," + this.graph.margin.top + ")"); - - this.graph.focus = this.graph.svg.append("g").attr("class", "tooltip-focus"); - - this.graph.area = this.graph.svg.append("g") - .attr("class", "stack-area") - .attr("clip-path",`url(#area-clip${this.props.box.key})`); - - this.graph.line = this.graph.svg.append("g") - .attr("class", "line-plot") - .attr("clip-path",`url(#area-clip${this.props.box.key})`); - - - - this.graph.overlay = this.graph.svg.append("g").append("rect").attr("class", "tooltip-overlay").attr("width", this.graph.width).attr("height", this.graph.height); - - this.graph.svg.on("mouseover", function () { - let layer = that.refs.lineChartRoot.parentNode.parentNode.parentNode.parentNode.parentNode; - layer.style.zIndex = 9; - - let hoverLine = that.graph.focus.select("line.x-hover-line"); - if (hoverLine.size() > 0) { - hoverLine.style("display", "block"); - } - - let instanceMetricCount = {}; - for (let counterKey in that.state.counters) { - let thisOption = that.props.box.option.filter((d) => {return d.counterKey === counterKey})[0]; - if (!thisOption) { - break; - } - - for (let i = 0; i < that.props.objects.length; i++) { - const obj = that.props.objects[i]; - if (thisOption.familyName === obj.objFamily) { - if (!instanceMetricCount[obj.objHash]) { - instanceMetricCount[obj.objHash] = 0; - } - let color; - if (that.props.config.graph.color === "metric") { - color = InstanceColor.getMetricColor(thisOption.counterKey, that.props.config.colorType); - } else { - color = InstanceColor.getInstanceColors(that.props.config.colorType)[obj.objHash][(instanceMetricCount[obj.objHash]++) % 5]; - } - that.mouseOverObject(that.props.objects[i], thisOption, color); - } - } - } - - that.graph.focus.selectAll("circle").style("display", "block"); - - //that.props.showTooltip(); - }); - this.graph.svg.on("mouseout",() =>{ - - let layer = this.refs.lineChartRoot.parentNode.parentNode.parentNode.parentNode.parentNode; - layer.style.zIndex = 5; - this.graph.focus.selectAll("circle").style("display", "none"); - this.graph - .focus - .select("line.x-hover-line") - .style("display", "none"); - - this.props.hideTooltip(); - if(!this.props.timeFocus.keep) - this.props.setTimeFocus(false, null, that.props.box.key); - - - }); - - this.graph.bisector = d3.bisector(function (d) { - return d.time; - }).left; - - this.graph.svg.on("contextmenu",()=>{ - // console.log(d3.event.which); - d3.event.preventDefault(); - // e.preventDefault(); - if(!this.props.timeFocus.keep){ - //toggle - //tooltip hidel - that.graph.focus.select("line.x-hover-line").style("display","none"); - that.graph.focus.selectAll("circle").style("display","none"); - that.props.hideTooltip(); - } - this.props.setTimeFocus( - this.props.timeFocus.active, - this.props.timeFocus.time, - this.props.timeFocus.id, - !this.props.timeFocus.keep - ); - }); - - this.graph.svg.on("mousemove", function () { - - - - let tooltip = {}; - tooltip.lines = []; - - let xPos = d3.mouse(this)[0]; - let yPos = d3.mouse(this)[1]; - if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) { - let box = that.refs.lineChart.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode; - if (window.getComputedStyle) { - let style = getComputedStyle(box); - let transform = style.transform || style.webkitTransform || style.mozTransform; - let mat = transform.match(/^matrix3d\((.+)\)$/); - if (mat) return parseFloat(mat[1].split(', ')[13]); - mat = transform.match(/^matrix\((.+)\)$/); - let transformX = mat ? parseFloat(mat[1].split(', ')[4]) : 0; - let transformY = mat ? parseFloat(mat[1].split(', ')[5]) : 0; - xPos = xPos - transformX; - yPos = yPos - transformY; - } - } - - let x0 = that.graph.x.invert(xPos); - let timeFormat = d3.timeFormat(that.graph.fullTimeFormat); - - let instanceMetricCount = {}; - - for (let counterKey in that.state.counters) { - let thisOption = that.props.box.option.filter((d) => {return d.counterKey === counterKey})[0]; - let dataIndex = that.graph.bisector(that.state.counters[counterKey], x0, 0); - - if (!that.state.counters[counterKey][dataIndex]) { - break; - } - - if (tooltip.timeValue && (tooltip.timeValue < that.state.counters[counterKey][dataIndex].time)) { - - } else { - tooltip.time = timeFormat(that.state.counters[counterKey][dataIndex].time); - tooltip.timeValue = that.state.counters[counterKey][dataIndex].time; - } - - if (!thisOption) { - break; - } - that.counterSum = 0; - for (let i = 0; i < that.props.objects.length; i++) { - const obj = that.props.objects[i]; - if (thisOption.familyName === obj.objFamily) { - - if (!instanceMetricCount[obj.objHash]) { - instanceMetricCount[obj.objHash] = 0; - } - let color; - if (that.props.config.graph.color === "metric") { - color = InstanceColor.getMetricColor(thisOption.counterKey, that.props.config.colorType); - } else { - color = InstanceColor.getInstanceColors(that.props.config.colorType)[obj.objHash][(instanceMetricCount[obj.objHash]++) % 5]; - } - that.mouseMoveObject(that.props.objects[i], thisOption, counterKey, dataIndex, color, tooltip); - } - } - } - - let hoverLine = that.graph.focus.select("line.x-hover-line").style('display','block'); - if (hoverLine.size() < 1) { - hoverLine = that.graph.focus.append("line").attr("class", "x-hover-line hover-line").attr("y1", 0).attr("y2", that.graph.height); - } - - let xPosition = that.graph.x(tooltip.timeValue); - - if (tooltip.timeValue) { - hoverLine.attr("x1", xPosition); - hoverLine.attr("x2", xPosition); - } - - if (tooltip && tooltip.lines) { - for (let i = 0; i < tooltip.lines.length; i++) { - - if (!isNaN(tooltip.lines[i].value)) { - let circle = that.graph.focus.select("circle." + tooltip.lines[i].circleKey).style('display','block'); - if (circle.size() > 0) { - circle.attr("cx", xPosition); - circle.attr("cy", that.graph.y(tooltip.lines[i].value)); - } - } - } - } - - tooltip.chartType = that.chartType; - tooltip.counterSum = numeral(that.counterSum).format(that.props.config.numberFormat); - that.graph.currentTooltipTime = tooltip.timeValue; - - if(!that.props.timeFocus.keep){ - that.props.setTimeFocus(true,x0.getTime(),that.props.box.key); - } - that.props.showTooltip(xPos, yPos, that.graph.margin.left, that.graph.margin.top, tooltip); - - }); - - - //-- brush - // Add the brush feature using the d3.brush function - this.brush = d3.brushX() - .extent([[0, 0], [this.graph.width, this.graph.height]]) - .on("end", this.zoomBrush); - this.graph.svg.append("g").attr("class", "brush").call(this.brush); - - this.graphAxis(this.graph.width, this.graph.height, true); - - }; - - axisUp = () => { - this.graph.maxY = this.graph.maxY * 1.2; - this.graphAxis(this.graph.width, this.graph.height, false); - this.draw(); - }; - - axisDown = () => { - - this.graph.maxY = this.graph.maxY * 0.8; - if (this.graph.maxY < this.graph.autoMaxY) { - this.graph.maxY = this.graph.autoMaxY; - } - - this.graphAxis(this.graph.width, this.graph.height, false); - this.draw(); - }; - - stopProgation = (e) => { - e.stopPropagation(); - }; - - render() { - return ( -
-
-
-
- {this.state.noData &&
-
-
NO DATA RECEIVED
-
-
} -
- ); - } -} - -let mapStateToProps = (state) => { - return { - objects: state.target.objects, - config: state.config, - filterMap: state.target.filterMap, - timeFocus: state.timeFocus, - range: state.range, - }; -}; - -let mapDispatchToProps = (dispatch) => { - return { - setTimeFocus: (active, time, boxKey,keep) => dispatch(setTimeFocus(active, time, boxKey,keep)), - setRealTimeValue: (realTime, longTerm, value) => dispatch(setRealTimeValue(realTime, longTerm, value)), - setRangeDateHoursMinutes: (date, hours, minutes) => dispatch(setRangeDateHoursMinutes(date, hours, minutes)), - setRealTimeRangeStepValue: (realTime, longTerm, value, range, step) => dispatch(setRealTimeRangeStepValue(realTime, longTerm, value, range, step)), - setSearchCondition: (from, to, time) => dispatch(setSearchCondition(from, to, time)), - }; -}; - -const LineChart_BACKUP = connect(mapStateToProps, mapDispatchToProps)(LineChart); -export default withRouter(LineChart_BACKUP); From 63ec9b2d36e47579bb8f4aa5b90d7af075fcffe1 Mon Sep 17 00:00:00 2001 From: "yeosong.heo" Date: Sat, 26 Oct 2019 20:40:16 +0900 Subject: [PATCH 65/77] :bug: max y axis calc, time focus delay --- src/components/Paper/LineChart/Line.js | 11 ++--------- src/components/Paper/LineChart/LineChart.js | 20 ++------------------ 2 files changed, 4 insertions(+), 27 deletions(-) diff --git a/src/components/Paper/LineChart/Line.js b/src/components/Paper/LineChart/Line.js index 9588b0c..7537e91 100644 --- a/src/components/Paper/LineChart/Line.js +++ b/src/components/Paper/LineChart/Line.js @@ -109,8 +109,6 @@ class Line extends Component { } paint (data){ - - this.clearLine(); if (data.objects) { let instanceMetricCount = {}; @@ -205,8 +203,7 @@ class Line extends Component { drawTimeFocus=(isFixed=false,nextProps)=>{ if( isFixed ){ - if( Object.keys(this.props.counters).map(k => this.props.counters[k][0] ? this.props.counters[k][0].time : null).filter( t => this.props.timeFocus.time > t ).length ) { - let hoverLine = this.focus.selectAll("line.focus-line"); + let hoverLine = this.focus.selectAll("line.focus-line"); hoverLine.attr("x1", (d) => this.xScale(d)) .attr("x2", (d) => this.xScale(d)); @@ -223,17 +220,13 @@ class Line extends Component { .exit() .remove(); hoverLine.style("display","block"); - }else{ - // 해제 - this.props.setTimeFocus(false,null,null,false); - } }else if( nextProps.timeFocus.id !== this.props.box.key) { let hoverLine = this.focus.selectAll("line.focus-line"); hoverLine.attr("x1", (d) =>this.xScale(d)) .attr("x2", (d) =>this.xScale(d)); - hoverLine.data([this.props.timeFocus.time]) + hoverLine.data([nextProps.timeFocus.time]) .enter() .append("line") .attr("class", "focus-line focus-hover-line") diff --git a/src/components/Paper/LineChart/LineChart.js b/src/components/Paper/LineChart/LineChart.js index 04abe54..216f25d 100644 --- a/src/components/Paper/LineChart/LineChart.js +++ b/src/components/Paper/LineChart/LineChart.js @@ -4,13 +4,6 @@ import {connect} from 'react-redux'; import {withRouter} from 'react-router-dom'; import ServerDate from "../../../common/ServerDate"; import Line from "./Line"; -import { - setRangeDateHoursMinutes, - setRealTimeRangeStepValue, - setRealTimeValue, - setSearchCondition, - setTimeFocus -} from "../../../actions"; class LineChart extends Component { @@ -190,8 +183,8 @@ class LineChart extends Component { options : {...this.graph,type : nextProps.box.values['chartType']}, search : false }); - } + this.chartType = nextProps.box.values['chartType']; } removeObject(prevList, currentList){ const ret = []; @@ -449,15 +442,6 @@ let mapStateToProps = (state) => { }; }; -let mapDispatchToProps = (dispatch) => { - return { - setTimeFocus: (active, time, boxKey,keep) => dispatch(setTimeFocus(active, time, boxKey,keep)), - setRealTimeValue: (realTime, longTerm, value) => dispatch(setRealTimeValue(realTime, longTerm, value)), - setRangeDateHoursMinutes: (date, hours, minutes) => dispatch(setRangeDateHoursMinutes(date, hours, minutes)), - setRealTimeRangeStepValue: (realTime, longTerm, value, range, step) => dispatch(setRealTimeRangeStepValue(realTime, longTerm, value, range, step)), - setSearchCondition: (from, to, time) => dispatch(setSearchCondition(from, to, time)), - }; -}; -LineChart = connect(mapStateToProps, mapDispatchToProps)(LineChart); +LineChart = connect(mapStateToProps, null)(LineChart); export default withRouter(LineChart); From c2f1e63f5b4b1bb2a8bfde5eb49647e4702e697f Mon Sep 17 00:00:00 2001 From: "yeosong.heo" Date: Sat, 26 Oct 2019 23:35:10 +0900 Subject: [PATCH 66/77] =?UTF-8?q?:zap:=20search=20=EC=83=81=ED=83=9C=20?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EB=9D=BC=EC=9D=B8=20=EC=B0=A8=ED=8A=B8=20?= =?UTF-8?q?zoom=20in=20=ED=95=A0=20=EA=B2=BD=EC=9A=B0=20=EB=93=9C=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EA=B5=AC=EA=B0=84=20=ED=99=95=EB=8C=80=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=ED=95=98=EB=8F=84=EB=A1=9D=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Paper/LineChart/Line.js | 76 +++++++++++++------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/src/components/Paper/LineChart/Line.js b/src/components/Paper/LineChart/Line.js index 7537e91..21dc8e0 100644 --- a/src/components/Paper/LineChart/Line.js +++ b/src/components/Paper/LineChart/Line.js @@ -10,6 +10,7 @@ import connect from "react-redux/es/connect/connect"; import {withRouter} from "react-router-dom"; import * as d3 from "d3"; import * as _ from "lodash"; +import moment from 'moment'; import numeral from "numeral"; import InstanceColor from "../../../common/InstanceColor"; @@ -31,7 +32,9 @@ class Line extends Component { return; } //- realtime <=> search reset - if(nextProps.search !== this.props.search){ + const {type} = nextProps.options; + const thisType = this.props.options.type; + if(nextProps.range.realTime !== this.props.range.realTime || type !== thisType){ switch(this.props.options.type){ case "STACK AREA": this.removePathLine(false); @@ -41,46 +44,35 @@ class Line extends Component { } this.zoomReset(); } - if(nextProps.search){ - this.zoomReset(); - } -// box option change; - const {type} = nextProps.options; - const thisType = this.props.options.type; - - if( type !== thisType){ - switch(type){ - case "STACK AREA": - this.removePathLine(false); - break; - default: - this.removePathLine(true); - } +// search reset + if(!nextProps.range.realTime){ this.zoomReset(); } - if( nextProps.options !== this.props.options && !this.isZoom){ - this.changedOption(nextProps.options, nextProps); - } + const isResize = nextProps.options.width !== this.props.options.width || + nextProps.options.height !== this.props.options.height; +// - normal if(!this.isZoom) { + if (nextProps.options !== this.props.options) { + this.changedOption(nextProps.options, nextProps); + } this.paint(nextProps); - } - - const isResize = nextProps.options.width !== this.props.options.width || nextProps.options.height !== this.props.options.height; - - if(this.isZoom && this.zoomData && isResize) { +// - zoom state + }else if( this.zoomData && isResize) { this.changedOption({...this.zoomData.options , - width : nextProps.options.width, - height : nextProps.options.height, + width : nextProps.options.width, + height : nextProps.options.height, },this.zoomData); this.paint(this.zoomData); - } - if(nextProps.timeFocus.active && !nextProps.noData) { - this.drawTimeFocus(nextProps.timeFocus.keep, nextProps); } - if(!nextProps.timeFocus.active && !nextProps.noData) { - this.removeFocus(nextProps); + if(!nextProps.noData) { + if(nextProps.timeFocus.active) { + this.drawTimeFocus(nextProps.timeFocus.keep, nextProps); + } + if(!nextProps.timeFocus.active) { + this.removeFocus(nextProps); + } } }; removePathLine(isStack){ @@ -493,17 +485,25 @@ class Line extends Component { zoomBrush = () => { const extent = d3.event.selection; + const {realTime} = this.props.range; if(extent) { this.brushG.call(this.brush.move,null); - this.isZoom = true; const endTime = this.xScale.invert(extent[1]); const startTime = this.xScale.invert(extent[0]); - - this.zoomData = {...this.props,startTime : startTime.getTime(),endTime : endTime.getTime()} - - this.changedOption(this.zoomData.options,this.zoomData); - this.paint(this.zoomData); - }else{ + if(realTime) { + this.isZoom = true; + this.zoomData = {...this.props, startTime: startTime.getTime(), endTime: endTime.getTime()} + this.changedOption(this.zoomData.options, this.zoomData); + this.paint(this.zoomData); + }else{ + const startDate= moment(startTime); + this.props.setRealTimeRangeStepValue(false, this.props.range.longTerm, + this.props.range.value, + this.props.config.range.shortHistoryRange, this.props.config.range.shortHistoryStep); + this.props.setRangeDateHoursMinutes(startDate, startDate.hours(), startDate.minutes()); + this.props.setSearchCondition(startTime.valueOf(), endTime.valueOf(), new Date().getTime()); + } + }else if(realTime) { this.isZoom = false; // restore this.changedOption(this.props.options,this.props); From 32de0931e9cdd957f771d26d4bd3709c1dc685b3 Mon Sep 17 00:00:00 2001 From: "yeosong.heo" Date: Sat, 26 Oct 2019 23:56:06 +0900 Subject: [PATCH 67/77] :bug: tooltip circle move --- src/components/Paper/LineChart/Line.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/components/Paper/LineChart/Line.js b/src/components/Paper/LineChart/Line.js index 21dc8e0..fe67810 100644 --- a/src/components/Paper/LineChart/Line.js +++ b/src/components/Paper/LineChart/Line.js @@ -792,11 +792,16 @@ class Line extends Component { for (let i = 0; i < tooltip.lines.length; i++) { if (!isNaN(tooltip.lines[i].value)) { - let circle = that.focus.select("circle." + tooltip.lines[i].circleKey).style('display','block'); - if (circle.size() > 0) { + let circle = that.focus.select("circle." + tooltip.lines[i].circleKey) circle.attr("cx", xPosition); circle.attr("cy", that.yScale(tooltip.lines[i].value)); - } + + circle.data([tooltip.lines[i].value]) + .enter() + .style('display','block') + .exit() + .remove(); + } } } From 30413dd35cb016f6c1e3288b2a3d6979a7809f45 Mon Sep 17 00:00:00 2001 From: "yeosong.heo" Date: Sun, 27 Oct 2019 00:26:56 +0900 Subject: [PATCH 68/77] =?UTF-8?q?:bug:=20=EB=9D=BC=EC=9D=B8=20=EC=B0=A8?= =?UTF-8?q?=ED=8A=B8=20mouse=20move=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=8A=90=EB=A6=B0=20=ED=98=84=EC=83=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Paper/LineChart/Line.js | 28 +++++++++++---------- src/components/Paper/LineChart/LineChart.js | 3 +-- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/components/Paper/LineChart/Line.js b/src/components/Paper/LineChart/Line.js index fe67810..0e06f65 100644 --- a/src/components/Paper/LineChart/Line.js +++ b/src/components/Paper/LineChart/Line.js @@ -56,7 +56,9 @@ class Line extends Component { if (nextProps.options !== this.props.options) { this.changedOption(nextProps.options, nextProps); } - this.paint(nextProps); + if (nextProps.counters !== this.props.counters) { + this.paint(nextProps); + } // - zoom state }else if( this.zoomData && isResize) { this.changedOption({...this.zoomData.options , @@ -439,16 +441,19 @@ class Line extends Component { changedOption(changed,props){ - // if(changed.width !== this.props.options.width || changed.height !== this.props.options.height ) { - this.brush.extent([[0, 0], [changed.width, changed.height]]); - this.brushG.call(this.brush); - this.area_clip - .attr("width", changed.width) - .attr("height", changed.height); - this.xScale = this.xScale.range([0, changed.width]); - this.yScale = this.yScale.range([changed.height, 0]); + if(changed.width !== this.props.options.width || changed.height !== this.props.options.height ) { + this.brush.extent([[0, 0], [changed.width, changed.height]]); + this.brushG.call(this.brush); + this.area_clip + .attr("width", changed.width) + .attr("height", changed.height); + this.xScale = this.xScale.range([0, changed.width]); + this.yScale = this.yScale.range([changed.height, 0]); + } + this.xScale.domain([props.startTime, props.endTime]); this.yScale.domain([0, props.options.maxY]); + let xAxisCount = Math.floor(changed.width / changed.xAxisWidth); if (xAxisCount < 1) { xAxisCount = 1; @@ -457,6 +462,7 @@ class Line extends Component { if (yAxisCount < 1) { yAxisCount = 1; } + // Y축 this.tickY.ticks(yAxisCount); @@ -820,12 +826,8 @@ class Line extends Component { }); this.svg.on("contextmenu",()=>{ - // console.log(d3.event.which); d3.event.preventDefault(); - // e.preventDefault(); if(!this.props.timeFocus.keep){ - //toggle - //tooltip hidel this.focus.select("line.x-hover-line").style("display","none"); this.focus.selectAll("circle").style("display","none"); this.props.hideTooltip(); diff --git a/src/components/Paper/LineChart/LineChart.js b/src/components/Paper/LineChart/LineChart.js index 216f25d..614e033 100644 --- a/src/components/Paper/LineChart/LineChart.js +++ b/src/components/Paper/LineChart/LineChart.js @@ -78,8 +78,7 @@ class LineChart extends Component { componentWillReceiveProps(nextProps) { let counters = Object.assign({}, this.state.counters); - - + console.log("------------------>"); if (nextProps.countersHistory && this.lastCountersHistoryTime !== nextProps.countersHistoryTimestamp) { this.lastCountersHistoryTime = nextProps.countersHistoryTimestamp; From 626c188649100a17476abb3835cdc75c43b4086f Mon Sep 17 00:00:00 2001 From: "yeosong.heo" Date: Sun, 27 Oct 2019 00:32:29 +0900 Subject: [PATCH 69/77] :fire: linechart debugging event --- src/components/Paper/LineChart/LineChart.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/Paper/LineChart/LineChart.js b/src/components/Paper/LineChart/LineChart.js index 614e033..2ec85e4 100644 --- a/src/components/Paper/LineChart/LineChart.js +++ b/src/components/Paper/LineChart/LineChart.js @@ -77,8 +77,6 @@ class LineChart extends Component { componentWillReceiveProps(nextProps) { let counters = Object.assign({}, this.state.counters); - - console.log("------------------>"); if (nextProps.countersHistory && this.lastCountersHistoryTime !== nextProps.countersHistoryTimestamp) { this.lastCountersHistoryTime = nextProps.countersHistoryTimestamp; From 5da9850c7d0ec3b07b492a05b20a324a5adfea28 Mon Sep 17 00:00:00 2001 From: "yeosong.heo" Date: Sun, 27 Oct 2019 01:32:15 +0900 Subject: [PATCH 70/77] =?UTF-8?q?:bug:=20=EC=84=9C=EC=B9=98=20=EC=8B=9C=20?= =?UTF-8?q?=EB=9D=BC=EC=9D=B8=EC=9D=B4=20=EB=B3=B4=EC=97=AC=EC=A7=80=20?= =?UTF-8?q?=EC=A7=80=EC=A7=80=20=EC=95=8A=EC=95=98=EB=8D=98=20=ED=98=84?= =?UTF-8?q?=EC=83=81=20=EC=88=98=EC=A0=95,=20=EC=8A=A4=ED=83=9DArea=20?= =?UTF-8?q?=ED=88=B4=ED=8C=81=20=ED=91=9C=EC=8B=9C=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Paper/LineChart/Line.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/Paper/LineChart/Line.js b/src/components/Paper/LineChart/Line.js index 0e06f65..90de5ff 100644 --- a/src/components/Paper/LineChart/Line.js +++ b/src/components/Paper/LineChart/Line.js @@ -34,17 +34,16 @@ class Line extends Component { //- realtime <=> search reset const {type} = nextProps.options; const thisType = this.props.options.type; - if(nextProps.range.realTime !== this.props.range.realTime || type !== thisType){ + if( type !== thisType){ switch(this.props.options.type){ case "STACK AREA": - this.removePathLine(false); + this.removePathLine(true); break; default: - this.removePathLine(true); + this.removePathLine(false); } this.zoomReset(); } -// search reset if(!nextProps.range.realTime){ this.zoomReset(); } @@ -56,7 +55,8 @@ class Line extends Component { if (nextProps.options !== this.props.options) { this.changedOption(nextProps.options, nextProps); } - if (nextProps.counters !== this.props.counters) { + if ((!nextProps.range.realTime && !nextProps.timeFocus.active) || + nextProps.counters !== this.props.counters) { this.paint(nextProps); } // - zoom state @@ -544,7 +544,7 @@ class Line extends Component { let valueOutput = obj.objHash && this.props.counters[counterKey][dataIndex].data[obj.objHash] ? this.props.counters[counterKey][dataIndex].data[obj.objHash].value : null ; const valueOrigin = obj.objHash && this.props.counters[counterKey][dataIndex].data[obj.objHash] ? this.props.counters[counterKey][dataIndex].data[obj.objHash].value : null ; - if( this.chartType === "STACK AREA" && valueOutput ){ + if( this.props.options.type === "STACK AREA" && valueOutput ){ valueOutput = this.counterSum + valueOutput; this.counterSum = valueOutput; } @@ -798,7 +798,7 @@ class Line extends Component { for (let i = 0; i < tooltip.lines.length; i++) { if (!isNaN(tooltip.lines[i].value)) { - let circle = that.focus.select("circle." + tooltip.lines[i].circleKey) + let circle = that.focus.select("circle." + tooltip.lines[i].circleKey); circle.attr("cx", xPosition); circle.attr("cy", that.yScale(tooltip.lines[i].value)); From f0bd0f4744f50b5cbdd86f6fc1a94478cb667123 Mon Sep 17 00:00:00 2001 From: "yeosong.heo" Date: Sun, 27 Oct 2019 01:45:15 +0900 Subject: [PATCH 71/77] =?UTF-8?q?:bug:=20=EC=B0=A8=ED=8A=B8=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EB=B3=80=EA=B2=BD=EC=8B=9C=20=EB=B3=B4=EC=97=AC?= =?UTF-8?q?=EC=A3=BC=EC=A7=80=20=EB=AA=BB=ED=95=98=EB=8A=94=20=ED=98=84?= =?UTF-8?q?=EC=83=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Paper/LineChart/Line.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/Paper/LineChart/Line.js b/src/components/Paper/LineChart/Line.js index 90de5ff..86e849f 100644 --- a/src/components/Paper/LineChart/Line.js +++ b/src/components/Paper/LineChart/Line.js @@ -43,6 +43,9 @@ class Line extends Component { this.removePathLine(false); } this.zoomReset(); + + this.changedOption(nextProps.options,nextProps); + this.paint(nextProps); } if(!nextProps.range.realTime){ this.zoomReset(); From 81dd98ef128905df6da0e3985fcb17c68ea5b615 Mon Sep 17 00:00:00 2001 From: "yeosong.heo" Date: Sun, 27 Oct 2019 23:37:46 +0900 Subject: [PATCH 72/77] =?UTF-8?q?:bug:=20chart=20type=20change=20=EC=8B=9C?= =?UTF-8?q?=20=EA=B8=B0=EC=A1=B4=20=EC=B0=A8=ED=8A=B8=EA=B0=80=20=EA=B7=B8?= =?UTF-8?q?=EB=A0=A4=EC=A7=80=EC=A7=80=20=EC=95=8A=EB=8A=94=20=ED=98=84?= =?UTF-8?q?=EC=83=81=20=EC=88=98=EC=A0=95,=20ToolTip=20=EC=B0=A8=ED=83=80?= =?UTF-8?q?=20TICKS=20=EB=84=98=EC=96=B4=EA=B0=80=EB=8A=94=20=ED=98=84?= =?UTF-8?q?=EC=83=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Paper/LineChart/Line.js | 102 +++++++++---------------- 1 file changed, 34 insertions(+), 68 deletions(-) diff --git a/src/components/Paper/LineChart/Line.js b/src/components/Paper/LineChart/Line.js index 86e849f..8d5f16f 100644 --- a/src/components/Paper/LineChart/Line.js +++ b/src/components/Paper/LineChart/Line.js @@ -35,17 +35,17 @@ class Line extends Component { const {type} = nextProps.options; const thisType = this.props.options.type; if( type !== thisType){ - switch(this.props.options.type){ - case "STACK AREA": - this.removePathLine(true); + switch(thisType){ + case 'STACK AREA': + this.stackArea.selectAll('path').remove(); break; default: - this.removePathLine(false); + this.line.selectAll('path').remove(); } this.zoomReset(); - this.changedOption(nextProps.options,nextProps); this.paint(nextProps); + } if(!nextProps.range.realTime){ this.zoomReset(); @@ -80,30 +80,6 @@ class Line extends Component { } } }; - removePathLine(isStack){ - if(!isStack) { - for (const obj of this.props.objects) { - - for (let counterKey in this.props.counters) { - let areaClass = "area-" + obj.objHash + "-" + this.replaceName(counterKey); - let lineClass = "line-" + obj.objHash + "-" + this.replaceName(counterKey); - this.line.selectAll("path." + areaClass) - .transition() - .delay(100) - .remove(); - this.line.selectAll("path." + lineClass) - .transition() - .delay(100) - .remove(); - } - } - }else{ - this.stackArea.selectAll('path.line') - .transition() - .delay(100) - .remove(); - } - } paint (data){ this.clearLine(); @@ -197,46 +173,36 @@ class Line extends Component { this.focus.select("line.focus-line").remove(); } } + focusLine=(time,y2)=>{ + let hoverLine = this.focus.selectAll("line.focus-line"); + const xfuc = (d) => { + const x = this.xScale(d); + if(x < 0){ + return 0; + } + if(x > this.props.options.width){ + return this.props.options.width; + } + return x; + }; + hoverLine.attr("x1",xfuc) + .attr("x2",xfuc); + + hoverLine.data([time]) + .enter() + .append("line") + .attr("class", "focus-line focus-hover-line") + .attr("y1", 0) + .attr("y2", y2) + .attr("x1", xfuc) + .attr("x2",xfuc) + .exit() + .remove(); + hoverLine.style("display","block"); + }; drawTimeFocus=(isFixed=false,nextProps)=>{ - - if( isFixed ){ - let hoverLine = this.focus.selectAll("line.focus-line"); - hoverLine.attr("x1", (d) => this.xScale(d)) - .attr("x2", (d) => this.xScale(d)); - - hoverLine.data([this.props.timeFocus.time]) - .enter() - .append("line") - .attr("class", "focus-line focus-hover-line") - .attr("y1", 0) - .attr("y2", nextProps.options.height) - .attr("x1", (d) => { - return this.xScale(d); - }) - .attr("x2", (d) => this.xScale(d)) - .exit() - .remove(); - hoverLine.style("display","block"); - - }else if( nextProps.timeFocus.id !== this.props.box.key) { - let hoverLine = this.focus.selectAll("line.focus-line"); - hoverLine.attr("x1", (d) =>this.xScale(d)) - .attr("x2", (d) =>this.xScale(d)); - - hoverLine.data([nextProps.timeFocus.time]) - .enter() - .append("line") - .attr("class", "focus-line focus-hover-line") - .attr("y1", 0) - .attr("y2", nextProps.options.height) - .attr("x1", (d) =>{ - return this.xScale(d); - }) - .attr("x2", (d) =>this.xScale(d)) - .exit() - .remove(); - hoverLine.style("display","block"); - + if( isFixed || nextProps.timeFocus.id !== this.props.box.key) { + this.focusLine(this.props.timeFocus.time,nextProps.options.height); }else{ this.focus.select("line.focus-line").remove(); } From 8620414aa8267a80f9f64cf36c264f8bc296d6ee Mon Sep 17 00:00:00 2001 From: "yeosong.heo" Date: Mon, 28 Oct 2019 00:05:15 +0900 Subject: [PATCH 73/77] =?UTF-8?q?:bug:=20=ED=83=80=EC=9E=84=ED=8F=AC?= =?UTF-8?q?=EC=BB=A4=EC=8A=A4=20=EB=9D=BC=EC=9D=B8=20=EC=B0=A8=ED=8A=B8=20?= =?UTF-8?q?=EB=82=B4=EC=97=90=EC=84=9C=20=EB=84=98=EC=96=B4=EA=B0=80?= =?UTF-8?q?=EB=8A=94=20=ED=98=84=EC=83=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Paper/LineChart/LineChart.js | 9 ++++++- src/components/Paper/XLog/XLog.js | 30 ++++++++++++--------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/components/Paper/LineChart/LineChart.js b/src/components/Paper/LineChart/LineChart.js index 2ec85e4..afc58eb 100644 --- a/src/components/Paper/LineChart/LineChart.js +++ b/src/components/Paper/LineChart/LineChart.js @@ -112,7 +112,7 @@ class LineChart extends Component { startTime: startTime, endTime: endTime, search : true, - options : {...this.graph,type : this.chartType}, + options : {...this.graph,type : nextProps.box.values['chartType']}, }); } @@ -181,7 +181,14 @@ class LineChart extends Component { search : false }); } + + if(nextProps.box.values['chartType'] !== this.chartType ) { + this.setState({ + options : {...this.graph,type : nextProps.box.values['chartType']} + }); + } this.chartType = nextProps.box.values['chartType']; + } removeObject(prevList, currentList){ const ret = []; diff --git a/src/components/Paper/XLog/XLog.js b/src/components/Paper/XLog/XLog.js index b4fac88..b09e48a 100644 --- a/src/components/Paper/XLog/XLog.js +++ b/src/components/Paper/XLog/XLog.js @@ -203,10 +203,20 @@ class XLog extends Component { drawTimeFocus = (isFixed = false) => { + const xfuc = (d) => { + const x = this.graph.x(d); + if(x < 0){ + return 0; + } + if(x > this.graph.width){ + return this.graph.width; + } + return x; + }; if( isFixed && !this.state.noData){ let hoverLine = this.graph.focus.selectAll("line.focus-line"); - hoverLine.attr("x1", (d) =>this.graph.x(d)) - .attr("x2", (d) =>this.graph.x(d)); + hoverLine.attr("x1",xfuc) + .attr("x2",xfuc); hoverLine.data([this.props.timeFocus.time]) .enter() @@ -214,17 +224,15 @@ class XLog extends Component { .attr("class", "focus-line focus-hover-line") .attr("y1", 0) .attr("y2", this.graph.height) - .attr("x1", (d) =>{ - return this.graph.x(d); - }) - .attr("x2", (d) =>this.graph.x(d)) + .attr("x1",xfuc) + .attr("x2",xfuc) .exit() .remove(); } else if( !this.state.noData && this.props.timeFocus.id && this.props.timeFocus.id !== this.props.box.key) { let hoverLine = this.graph.focus.selectAll("line.focus-line"); - hoverLine.attr("x1", (d) =>this.graph.x(d)) - .attr("x2", (d) =>this.graph.x(d)); + hoverLine.attr("x1",xfuc) + .attr("x2",xfuc); hoverLine.data([this.props.timeFocus.time]) .enter() @@ -232,10 +240,8 @@ class XLog extends Component { .attr("class", "focus-line focus-hover-line") .attr("y1", 0) .attr("y2", this.graph.height) - .attr("x1", (d) =>{ - return this.graph.x(d); - }) - .attr("x2", (d) =>this.graph.x(d)) + .attr("x1",xfuc) + .attr("x2",xfuc) .exit() .remove(); From cedd2b8c06d03d84d97cbd620abf73628189cbeb Mon Sep 17 00:00:00 2001 From: "yeosong.heo" Date: Mon, 28 Oct 2019 00:14:16 +0900 Subject: [PATCH 74/77] =?UTF-8?q?:bug:=20XLOG=20=ED=83=80=EC=9E=84?= =?UTF-8?q?=ED=8F=AC=EC=BB=A4=EC=8A=A4=20=EB=9D=BC=EC=9D=B8=20=EC=B0=A8?= =?UTF-8?q?=ED=8A=B8=20=EB=82=B4=EC=97=90=EC=84=9C=20=EB=84=98=EC=96=B4?= =?UTF-8?q?=EA=B0=80=EB=8A=94=20=ED=98=84=EC=83=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Paper/XLog/XLog.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/components/Paper/XLog/XLog.js b/src/components/Paper/XLog/XLog.js index b09e48a..f4a15ec 100644 --- a/src/components/Paper/XLog/XLog.js +++ b/src/components/Paper/XLog/XLog.js @@ -59,9 +59,12 @@ class XLog extends Component { if (this.props.layoutChangeTime !== nextProps.layoutChangeTime) { this.graphResize(); } - if(!this.props.timeFocus.keep) { + if(!nextProps.timeFocus.keep) { this.drawTimeFocus(); } + if(!nextProps.timeFocus.active) { + this.removeFocus(nextProps); + } } shouldComponentUpdate(nextProps, nextState) { @@ -198,9 +201,11 @@ class XLog extends Component { } }, 100); }; - - - + removeFocus(nextProps){ + if(nextProps.timeFocus.id !== this.props.box.key) { + this.graph.focus.select("line.focus-line").remove(); + } + } drawTimeFocus = (isFixed = false) => { const xfuc = (d) => { From 9e1ab0ab231dcecd26dde03f8937e475e89527c8 Mon Sep 17 00:00:00 2001 From: kranian Date: Mon, 28 Oct 2019 09:43:13 +0900 Subject: [PATCH 75/77] =?UTF-8?q?:bug:=20=EC=B0=A8=ED=8A=B8=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EB=B3=80=EA=B2=BD=20=ED=9B=84=20=EC=9D=B4=EC=A0=84?= =?UTF-8?q?=20tooltip=20cycle=20point=20=EA=B0=80=20=EB=82=A8=EC=95=84=20?= =?UTF-8?q?=EC=9E=88=EB=8A=94=20=ED=98=84=EC=83=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Paper/LineChart/Line.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/Paper/LineChart/Line.js b/src/components/Paper/LineChart/Line.js index 8d5f16f..68f4622 100644 --- a/src/components/Paper/LineChart/Line.js +++ b/src/components/Paper/LineChart/Line.js @@ -42,13 +42,16 @@ class Line extends Component { default: this.line.selectAll('path').remove(); } + this.focus.selectAll("circle").remove(); this.zoomReset(); this.changedOption(nextProps.options,nextProps); this.paint(nextProps); } + if(!nextProps.range.realTime){ this.zoomReset(); + } const isResize = nextProps.options.width !== this.props.options.width || From c81f230ae446d97d18ffd7a0ebc43f0fcdb37e9d Mon Sep 17 00:00:00 2001 From: mindplates Date: Mon, 28 Oct 2019 22:26:30 +0900 Subject: [PATCH 76/77] =?UTF-8?q?-=20=EB=B2=84=EC=A0=84=20=EC=97=85=20-=20?= =?UTF-8?q?=EB=94=94=EC=8A=A4=ED=81=AC=20=EC=82=AC=EC=9A=A9=20=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=20=ED=97=A4=EB=8D=94=20=EA=B3=A0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/common.js | 2 +- src/components/Paper/DiskUsage/DiskUsage.css | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/common/common.js b/src/common/common.js index cc4ce64..e4e6b70 100644 --- a/src/common/common.js +++ b/src/common/common.js @@ -1,7 +1,7 @@ // local storage access import moment from "moment"; import {Dictionary, DictType} from "./dictionary"; -export const version = "2.5.0"; +export const version = "2.6.0"; export function getData(key) { let ls = null; diff --git a/src/components/Paper/DiskUsage/DiskUsage.css b/src/components/Paper/DiskUsage/DiskUsage.css index 8e2400b..77e88cf 100644 --- a/src/components/Paper/DiskUsage/DiskUsage.css +++ b/src/components/Paper/DiskUsage/DiskUsage.css @@ -14,9 +14,11 @@ .disk-usage-list > div.row.table-title { display: table; background-color: #26466d; - color: white; + background-color: #F2F2F2; text-align: left; font-size: 10px; + position: sticky; + top: 0; } .disk-usage-list > div > span { From 619d42c3c823e48fd22676ffd231777486a93da9 Mon Sep 17 00:00:00 2001 From: mindplates Date: Mon, 28 Oct 2019 23:07:07 +0900 Subject: [PATCH 77/77] =?UTF-8?q?-=20Flow=20=EC=B0=BD=20=ED=81=AC=EA=B8=B0?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../XLog/Profiler/FrameProfile/FrameProfile.css | 14 +++++++++++--- .../Profiler/FrameProfile/XlogFlow/XlogFlow.css | 15 +++++++++++---- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.css b/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.css index a595cc7..7bf4a06 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.css +++ b/src/components/Paper/XLog/Profiler/FrameProfile/FrameProfile.css @@ -455,7 +455,7 @@ } .frame-profile .frame-xlog-flow { - position: absolute; + position: fixed; top: 0; left: 0; right: 0; @@ -477,8 +477,16 @@ box-sizing: border-box; } + .frame-profile .frame-xlog-flow > div > div { background-color: white; - height: calc(100% - 400px); - width: 100%; + height: calc(100% - 80px); + margin: 40px 30px; +} + +@media screen and (max-width: 800px) { + .frame-profile .frame-xlog-flow > div > div { + height: calc(100% - 20px); + margin: 10px 0; + } } \ No newline at end of file diff --git a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.css b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.css index 1264390..24a9f1b 100644 --- a/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.css +++ b/src/components/Paper/XLog/Profiler/FrameProfile/XlogFlow/XlogFlow.css @@ -40,12 +40,19 @@ width: 100%; height: 100%; vertical-align: middle; - padding: 0 10px; + padding: 0; position: relative; box-sizing: border-box; } .xlog-flow .frame-xlog-flow-content > div > div { background-color: #fff; - height: calc(100% - 60px); - width: 100%; -} + height: 80%; + margin: 0 40px; +} + +@media screen and (max-width: 800px) { + .xlog-flow .frame-xlog-flow-content > div > div { + height: 80%; + margin: 0; + } +} \ No newline at end of file