From aed6ce4c9b46123d1a1a73861a37e50338bdb69d Mon Sep 17 00:00:00 2001 From: aaajiulong <525763728@qq.com> Date: Tue, 26 Jan 2021 00:38:07 +0800 Subject: [PATCH 01/20] refactor: split function --- src/flask_state/services/host_status.py | 52 ++++++++++++++++--------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/src/flask_state/services/host_status.py b/src/flask_state/services/host_status.py index 601161a..30b8a35 100644 --- a/src/flask_state/services/host_status.py +++ b/src/flask_state/services/host_status.py @@ -32,23 +32,12 @@ def record_flask_state_host(interval, target_time): return try: - cpu = psutil.cpu_percent(interval=Config.CPU_PERCENT_INTERVAL) - memory = psutil.virtual_memory().percent - if platform.system() == "Windows": - load_avg = Config.DEFAULT_WINDOWS_LOAD_AVG - else: - load_avg = ",".join([str(float("%.2f" % x)) for x in os.getloadavg()]) - disk_usage = psutil.disk_usage("/").percent - boot_ts = psutil.boot_time() - result_conf = { - "ts": get_current_ms(), - "cpu": cpu, - "memory": memory, - "load_avg": load_avg, - "disk_usage": disk_usage, - "boot_seconds": int(get_current_s() - boot_ts), - } - result_conf.update(query_redis_info()) + result_conf = {} + host_status = query_host_info() + result_conf.update(host_status) + redis_status = query_redis_info() + result_conf.update(redis_status) + create_host_status(result_conf) now_time = get_current_s() new_day_utc = ( @@ -61,13 +50,38 @@ def record_flask_state_host(interval, target_time): logger.exception(e) +def query_host_info(): + """ + Collect host status + :return host status dict + :rtype: dict + """ + cpu = psutil.cpu_percent(interval=Config.CPU_PERCENT_INTERVAL) + memory = psutil.virtual_memory().percent + if platform.system() == "Windows": + load_avg = Config.DEFAULT_WINDOWS_LOAD_AVG + else: + load_avg = ",".join([str(float("%.2f" % x)) for x in os.getloadavg()]) + disk_usage = psutil.disk_usage("/").percent + boot_ts = psutil.boot_time() + result = { + "ts": get_current_ms(), + "cpu": cpu, + "memory": memory, + "load_avg": load_avg, + "disk_usage": disk_usage, + "boot_seconds": int(get_current_s() - boot_ts), + } + return result + + def query_redis_info(): """ Collect redis status :return: redis status dict :rtype: dict """ - result = dict() + result = {} redis_handler = redis_conn.get_redis() if redis_handler: try: @@ -91,7 +105,7 @@ def query_redis_info(): yesterday_keyspace_misses = yesterday_current_statistic.keyspace_misses if yesterday_keyspace_hits is not None and yesterday_keyspace_misses is not None: be_divided_num = ( - keyspace_hits + keyspace_misses - (yesterday_keyspace_hits + yesterday_keyspace_misses) + keyspace_hits + keyspace_misses - (yesterday_keyspace_hits + yesterday_keyspace_misses) ) delta_hits_ratio = ( float( From c83c742b16fc2a161659532f11f1222fbe0ead3a Mon Sep 17 00:00:00 2001 From: aaajiulong <525763728@qq.com> Date: Tue, 26 Jan 2021 00:43:36 +0800 Subject: [PATCH 02/20] style: Modify the latest host status acquisition logic --- src/flask_state/services/host_status.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/flask_state/services/host_status.py b/src/flask_state/services/host_status.py index 30b8a35..55e476a 100644 --- a/src/flask_state/services/host_status.py +++ b/src/flask_state/services/host_status.py @@ -144,8 +144,13 @@ def query_flask_state_host(days) -> FlaskStateResponse: if days not in TimeConstants.DAYS_SCOPE: raise FlaskStateError(**MsgCode.OVERSTEP_DAYS_SCOPE.value, status_code=HTTPStatus.BAD_REQUEST) - current_status = retrieve_latest_host_status() - current_status["load_avg"] = (current_status.get("load_avg") or "").split(",") + try: + current_status = query_host_info() + current_status.update(query_redis_info()) + current_status["load_avg"] = (current_status.get("load_avg") or "").split(",") + except: + current_status = retrieve_latest_host_status() + current_status["load_avg"] = (current_status.get("load_avg") or "").split(",") result = retrieve_host_status(days) result = control_result_counts(result) arr = [] From 71991bc28877a6f1315e3b05431a1af2b9afe489 Mon Sep 17 00:00:00 2001 From: aaajiulong <525763728@qq.com> Date: Tue, 26 Jan 2021 11:23:52 +0800 Subject: [PATCH 03/20] style: Extend the redis timeout period --- src/flask_state/conf/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/flask_state/conf/config.py b/src/flask_state/conf/config.py index ed55033..8519325 100644 --- a/src/flask_state/conf/config.py +++ b/src/flask_state/conf/config.py @@ -1,7 +1,7 @@ class Config: """ Config """ - REDIS_CONNECT_TIMEOUT = 1 # Redis socket connection timeout + REDIS_CONNECT_TIMEOUT = 3 # Redis socket connection timeout REDIS_TIMEOUT = 5 # Redis socket timeout CPU_PERCENT_INTERVAL = 0 # Time interval to calculate CPU utilization using psutil DEFAULT_BIND_SQLITE = "flask_state_sqlite" # Default binding database URL key From 33b15a6c29f452624f7ae12464bf9d75d249dca9 Mon Sep 17 00:00:00 2001 From: aaajiulong <525763728@qq.com> Date: Tue, 26 Jan 2021 14:52:07 +0800 Subject: [PATCH 04/20] style: Add missing redis warning message --- src/flask_state/exceptions/log_msg.py | 1 + src/flask_state/services/__init__.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/flask_state/exceptions/log_msg.py b/src/flask_state/exceptions/log_msg.py index 218d2e9..2fa86d1 100644 --- a/src/flask_state/exceptions/log_msg.py +++ b/src/flask_state/exceptions/log_msg.py @@ -28,6 +28,7 @@ class ErrorMsg(ExceptionMsg): class WarningMsg(ExceptionMsg): TIME_SMALL = {"msg": "Setting the recording time is too short", "level": "warning"} + LACK_REDIS = {"msg": "Redis module is not installed", "level": "warning"} class InfoMsg(ExceptionMsg): diff --git a/src/flask_state/services/__init__.py b/src/flask_state/services/__init__.py index 5317fb6..c82c618 100644 --- a/src/flask_state/services/__init__.py +++ b/src/flask_state/services/__init__.py @@ -1,4 +1,6 @@ from ..conf.config import Config +from ..exceptions.log_msg import WarningMsg +from ..utils.logger import logger # Create redis object @@ -18,6 +20,7 @@ def set_redis(self, redis_conf): socket_timeout=Config.REDIS_TIMEOUT, ) except ImportError: + logger.warning(WarningMsg.LACK_REDIS.get_msg()) self.redis = None def get_redis(self): From 5b847bb27fb0f08382282da7233165eea081e166 Mon Sep 17 00:00:00 2001 From: aaajiulong <525763728@qq.com> Date: Wed, 27 Jan 2021 01:02:01 +0800 Subject: [PATCH 05/20] feature: Add traffic collection --- src/flask_state/models/flask_state_host.py | 3 +++ src/flask_state/services/host_status.py | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/src/flask_state/models/flask_state_host.py b/src/flask_state/models/flask_state_host.py index 063eb4d..5482258 100644 --- a/src/flask_state/models/flask_state_host.py +++ b/src/flask_state/models/flask_state_host.py @@ -1,5 +1,6 @@ from sqlalchemy import func from sqlalchemy.sql import text +from sqlalchemy.dialects.mysql import INTEGER from ..conf.config import Config from . import db @@ -20,6 +21,8 @@ class FlaskStateHost(db.Model): load_avg = db.Column(db.String(32), server_default="") disk_usage = db.Column(db.Float, server_default=text("0")) boot_seconds = db.Column(db.Integer, server_default=text("0")) + net_sent = db.Column(INTEGER(unsigned=True), server_default=text("0")) + net_recv = db.Column(INTEGER(unsigned=True), server_default=text("0")) ts = db.Column(db.Integer, server_default=text("0")) # redis diff --git a/src/flask_state/services/host_status.py b/src/flask_state/services/host_status.py index 55e476a..263de25 100644 --- a/src/flask_state/services/host_status.py +++ b/src/flask_state/services/host_status.py @@ -58,6 +58,8 @@ def query_host_info(): """ cpu = psutil.cpu_percent(interval=Config.CPU_PERCENT_INTERVAL) memory = psutil.virtual_memory().percent + net_sent = psutil.net_io_counters().bytes_sent + net_recv = psutil.net_io_counters().bytes_recv if platform.system() == "Windows": load_avg = Config.DEFAULT_WINDOWS_LOAD_AVG else: @@ -71,6 +73,8 @@ def query_host_info(): "load_avg": load_avg, "disk_usage": disk_usage, "boot_seconds": int(get_current_s() - boot_ts), + "net_sent": net_sent, + "net_recv": net_recv, } return result From 9860a2c80ce3c5d3448d759992576c857aff541b Mon Sep 17 00:00:00 2001 From: aaajiulong <525763728@qq.com> Date: Fri, 5 Feb 2021 15:49:14 +0800 Subject: [PATCH 06/20] feature: Sub-table and add disk io, network io monitoring --- src/flask_state/conf/config.py | 1 + src/flask_state/dao/host_status.py | 15 ++++++- src/flask_state/models/flask_state_host.py | 45 +++++++++---------- src/flask_state/models/flask_state_io.py | 41 +++++++++++++++++ src/flask_state/services/host_status.py | 52 +++++++++++++++++++--- 5 files changed, 125 insertions(+), 29 deletions(-) create mode 100644 src/flask_state/models/flask_state_io.py diff --git a/src/flask_state/conf/config.py b/src/flask_state/conf/config.py index 8519325..6f4687c 100644 --- a/src/flask_state/conf/config.py +++ b/src/flask_state/conf/config.py @@ -10,3 +10,4 @@ class Config: DEFAULT_WINDOWS_LOAD_AVG = "0, 0, 0" # Windows system cannot calculate load AVG MAX_RETURN_RECORDS = 480 # Return the maximum number of records ABANDON_THRESHOLD = 60 # Maximum timeout time of scheduled tasks + ABANDON_IO_THRESHOLD = 10 # Maximum timeout time of scheduled tasks diff --git a/src/flask_state/dao/host_status.py b/src/flask_state/dao/host_status.py index 1a66321..94a432a 100644 --- a/src/flask_state/dao/host_status.py +++ b/src/flask_state/dao/host_status.py @@ -1,6 +1,7 @@ from ..exceptions.log_msg import InfoMsg from ..models import db from ..models.flask_state_host import FlaskStateHost +from ..models.flask_state_io import FlaskStateIO from ..utils.date import get_current_ms, get_query_ms from ..utils.logger import logger @@ -43,7 +44,6 @@ def retrieve_latest_host_status() -> dict: def create_host_status(kwargs): """ Create a new record - """ try: flask_state_host = FlaskStateHost(**kwargs) @@ -55,6 +55,19 @@ def create_host_status(kwargs): raise e +def create_host_io(kwargs): + """ + Create a new io record + """ + try: + flask_state_io = FlaskStateIO(**kwargs) + db.session.add(flask_state_io) + db.session.commit() + except Exception as e: + db.session.rollback() + raise e + + def delete_thirty_days_status(): """ Delete thirty days records ago diff --git a/src/flask_state/models/flask_state_host.py b/src/flask_state/models/flask_state_host.py index 5482258..8dfd722 100644 --- a/src/flask_state/models/flask_state_host.py +++ b/src/flask_state/models/flask_state_host.py @@ -1,40 +1,39 @@ -from sqlalchemy import func +# -*- coding: utf-8 -*- + +from sqlalchemy import String, func +from sqlalchemy.dialects.mysql import BIGINT, DATETIME, FLOAT, INTEGER, SMALLINT from sqlalchemy.sql import text -from sqlalchemy.dialects.mysql import INTEGER from ..conf.config import Config from . import db -# model class FlaskStateHost(db.Model): __bind_key__ = Config.DEFAULT_BIND_SQLITE __tablename__ = "flask_state_host" - id = db.Column(db.Integer, autoincrement=True) - create_time = db.Column(db.DateTime, server_default=func.now()) - update_time = db.Column(db.DateTime, server_default=func.now(), onupdate=func.now()) + id = db.Column(INTEGER(unsigned=True), autoincrement=True) + create_time = db.Column(DATETIME, server_default=func.now()) + update_time = db.Column(DATETIME, server_default=func.now(), onupdate=func.now()) # host - cpu = db.Column(db.Float, server_default=text("0")) - memory = db.Column(db.Float, server_default=text("0")) - load_avg = db.Column(db.String(32), server_default="") - disk_usage = db.Column(db.Float, server_default=text("0")) - boot_seconds = db.Column(db.Integer, server_default=text("0")) - net_sent = db.Column(INTEGER(unsigned=True), server_default=text("0")) - net_recv = db.Column(INTEGER(unsigned=True), server_default=text("0")) - ts = db.Column(db.Integer, server_default=text("0")) + cpu = db.Column(FLOAT(unsigned=True), server_default=text("0")) + memory = db.Column(FLOAT(unsigned=True), server_default=text("0")) + load_avg = db.Column(String(32), server_default="") + disk_usage = db.Column(FLOAT(unsigned=True), server_default=text("0")) + boot_seconds = db.Column(INTEGER(unsigned=True), server_default=text("0")) + ts = db.Column(INTEGER(unsigned=True), server_default=text("0")) # redis - used_memory = db.Column(db.Integer, server_default=text("0")) - used_memory_rss = db.Column(db.Integer, server_default=text("0")) - connected_clients = db.Column(db.SmallInteger, server_default=text("0")) - uptime_in_seconds = db.Column(db.Integer, server_default=text("0")) - mem_fragmentation_ratio = db.Column(db.Float, server_default=text("0")) - keyspace_hits = db.Column(db.Integer, server_default=text("0")) - keyspace_misses = db.Column(db.Integer, server_default=text("0")) - hits_ratio = db.Column(db.Float, server_default=text("0")) - delta_hits_ratio = db.Column(db.Float, server_default=text("0")) + used_memory = db.Column(INTEGER(unsigned=True), server_default=text("0")) + used_memory_rss = db.Column(INTEGER(unsigned=True), server_default=text("0")) + connected_clients = db.Column(SMALLINT(unsigned=True), server_default=text("0")) + uptime_in_seconds = db.Column(INTEGER(unsigned=True), server_default=text("0")) + mem_fragmentation_ratio = db.Column(FLOAT(unsigned=True), server_default=text("0")) + keyspace_hits = db.Column(INTEGER(unsigned=True), server_default=text("0")) + keyspace_misses = db.Column(INTEGER(unsigned=True), server_default=text("0")) + hits_ratio = db.Column(FLOAT(unsigned=True), server_default=text("0")) + delta_hits_ratio = db.Column(FLOAT(unsigned=True), server_default=text("0")) __table_args__ = ( db.PrimaryKeyConstraint("id"), diff --git a/src/flask_state/models/flask_state_io.py b/src/flask_state/models/flask_state_io.py new file mode 100644 index 0000000..bffb985 --- /dev/null +++ b/src/flask_state/models/flask_state_io.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- + +from sqlalchemy import func +from sqlalchemy.dialects.mysql import BIGINT, DATETIME, FLOAT, INTEGER +from sqlalchemy.sql import text + +from ..conf.config import Config +from . import db + + +class FlaskStateIO(db.Model): + __bind_key__ = Config.DEFAULT_BIND_SQLITE + __tablename__ = "flask_state_io" + + id = db.Column(INTEGER(unsigned=True), autoincrement=True) + create_time = db.Column(DATETIME, server_default=func.now()) + update_time = db.Column(DATETIME, server_default=func.now(), onupdate=func.now()) + + # network + net_sent = db.Column(BIGINT(unsigned=True), server_default=text("0")) + net_recv = db.Column(BIGINT(unsigned=True), server_default=text("0")) + + # disk + disk_read_bytes = db.Column(BIGINT(unsigned=True), server_default=text("0")) + disk_write_bytes = db.Column(BIGINT(unsigned=True), server_default=text("0")) + + __table_args__ = ( + db.PrimaryKeyConstraint("id"), + db.Index("idx_ct", create_time.desc()), + { + "extend_existing": True, + }, + ) + + def __repr__(self): + return "".format( + self.net_sent, + self.net_recv, + self.disk_read_bytes, + self.disk_write_bytes, + ) diff --git a/src/flask_state/services/host_status.py b/src/flask_state/services/host_status.py index 263de25..83ef994 100644 --- a/src/flask_state/services/host_status.py +++ b/src/flask_state/services/host_status.py @@ -58,8 +58,6 @@ def query_host_info(): """ cpu = psutil.cpu_percent(interval=Config.CPU_PERCENT_INTERVAL) memory = psutil.virtual_memory().percent - net_sent = psutil.net_io_counters().bytes_sent - net_recv = psutil.net_io_counters().bytes_recv if platform.system() == "Windows": load_avg = Config.DEFAULT_WINDOWS_LOAD_AVG else: @@ -73,8 +71,6 @@ def query_host_info(): "load_avg": load_avg, "disk_usage": disk_usage, "boot_seconds": int(get_current_s() - boot_ts), - "net_sent": net_sent, - "net_recv": net_recv, } return result @@ -109,7 +105,7 @@ def query_redis_info(): yesterday_keyspace_misses = yesterday_current_statistic.keyspace_misses if yesterday_keyspace_hits is not None and yesterday_keyspace_misses is not None: be_divided_num = ( - keyspace_hits + keyspace_misses - (yesterday_keyspace_hits + yesterday_keyspace_misses) + keyspace_hits + keyspace_misses - (yesterday_keyspace_hits + yesterday_keyspace_misses) ) delta_hits_ratio = ( float( @@ -135,6 +131,52 @@ def query_redis_info(): return result +def record_flask_state_io_host(interval, target_time): + """ + Record local status and monitor redis status + + """ + if get_current_s() - target_time > Config.ABANDON_IO_THRESHOLD: + format_date = get_formatted_timestamp(target_time) + logger.error(ErrorMsg.RUN_TIME_ERROR.get_msg(". Target time is {}".format(format_date))) + return + + try: + result_conf = {} + host_io_status = query_host_io_info() + result_conf.update(host_io_status) + + create_host_status(result_conf) + now_time = get_current_s() + new_day_utc = ( + datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=timezone.utc).timestamp() + ) + if now_time <= new_day_utc + interval: + delete_thirty_days_status() + + except Exception as e: + logger.exception(e) + + +def query_host_io_info(): + """ + Collect host io status + :return host status dict + :rtype: dict + """ + net_sent = psutil.net_io_counters().bytes_sent + net_recv = psutil.net_io_counters().bytes_recv + disk_read = psutil.disk_io_counters().read_bytes + disk_write = psutil.disk_io_counters().write_bytes + result = { + "net_sent": net_sent, + "net_recv": net_recv, + "disk_read": disk_read, + "disk_write": disk_write, + } + return result + + def query_flask_state_host(days) -> FlaskStateResponse: """ Query the local status and redis status of [1,3,7,30] days From e0e3f3b361244c39f5bfd5c471e7ada52c4909a8 Mon Sep 17 00:00:00 2001 From: aaajiulong <525763728@qq.com> Date: Tue, 9 Feb 2021 17:59:02 +0800 Subject: [PATCH 07/20] feature: Add network io collection and disk io collection --- src/flask_state/controller/manager.py | 37 ++++++++++++++++++++---- src/flask_state/dao/host_status.py | 22 ++++++++++++++ src/flask_state/models/flask_state_io.py | 6 ++-- src/flask_state/services/host_status.py | 23 ++++++++++++--- src/flask_state/utils/file_lock.py | 8 ++--- 5 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/flask_state/controller/manager.py b/src/flask_state/controller/manager.py index 99cabb3..06dde31 100644 --- a/src/flask_state/controller/manager.py +++ b/src/flask_state/controller/manager.py @@ -12,7 +12,7 @@ from ..exceptions.log_msg import ErrorMsg, InfoMsg from ..models import model_init_app from ..services import redis_conn -from ..services.host_status import query_flask_state_host, record_flask_state_host +from ..services.host_status import query_flask_state_host, record_flask_state_host, record_flask_state_io_host from ..utils.auth import auth_method, auth_user from ..utils.constants import HttpMethod, HTTPStatus from ..utils.file_lock import Lock @@ -47,15 +47,19 @@ def init_app(app, interval=60, log_instance=None): ) # Timing recorder - t = threading.Thread( + t_host = threading.Thread( target=record_timer, args=( app, interval, ), ) - t.setDaemon(True) - t.start() + t_host.setDaemon(True) + t_host.start() + + t_io = threading.Thread(target=record_io_timer, args=(app, 10), ) + t_io.setDaemon(True) + t_io.start() def init_redis(app): @@ -79,7 +83,7 @@ def init_db(app): def record_timer(app, interval): - app.lock_flask_state = Lock.get_file_lock() + app.lock_flask_state = Lock.get_file_lock("host") with app.app_context(): try: current_app.lock_flask_state.acquire() @@ -102,6 +106,29 @@ def record_timer(app, interval): raise e +def record_io_timer(app, interval): + app.lock_flask_state = Lock.get_file_lock("io") + with app.app_context(): + try: + current_app.lock_flask_state.acquire() + + s = sched.scheduler(time.time, time.sleep) + in_time = time.time() + target_time = int(int((time.time()) / 60 + 1) * 60) + time.sleep(60 - in_time % 60) + record_flask_state_io_host(interval, target_time) + while True: + target_time += interval + now_time = time.time() + s.enter(target_time - now_time, 1, record_flask_state_io_host, (interval, target_time)) + s.run() + except BlockingIOError: + pass + except Exception as e: + current_app.lock_flask_state.release() + raise e + + @auth_user @auth_method @json_required diff --git a/src/flask_state/dao/host_status.py b/src/flask_state/dao/host_status.py index 94a432a..3314993 100644 --- a/src/flask_state/dao/host_status.py +++ b/src/flask_state/dao/host_status.py @@ -40,6 +40,14 @@ def retrieve_latest_host_status() -> dict: result = result._asdict() if result else {} return result +def retrieve_latest_io_status() -> dict: + """ + Query the latest io status + + """ + result = FlaskStateIO.query.with_entities(FlaskStateIO.__table__).order_by(FlaskStateIO.ts.desc()).first() + result = result._asdict() if result else {} + return result def create_host_status(kwargs): """ @@ -83,6 +91,20 @@ def delete_thirty_days_status(): db.session.rollback() raise e +def delete_thirty_days_io_status(): + """ + Delete thirty days io records ago + + """ + try: + target_time = get_current_ms() - get_query_ms(THIRTY_DAT) + result = FlaskStateIO.query.filter(FlaskStateIO.ts < target_time).delete(synchronize_session=False) + if result: + db.session.commit() + logger.info(InfoMsg.DELETE_SUCCESS.get_msg()) + except Exception as e: + db.session.rollback() + raise e def retrieve_host_status_yesterday() -> FlaskStateHost: """ diff --git a/src/flask_state/models/flask_state_io.py b/src/flask_state/models/flask_state_io.py index bffb985..0750d76 100644 --- a/src/flask_state/models/flask_state_io.py +++ b/src/flask_state/models/flask_state_io.py @@ -21,9 +21,9 @@ class FlaskStateIO(db.Model): net_recv = db.Column(BIGINT(unsigned=True), server_default=text("0")) # disk - disk_read_bytes = db.Column(BIGINT(unsigned=True), server_default=text("0")) - disk_write_bytes = db.Column(BIGINT(unsigned=True), server_default=text("0")) - + disk_read = db.Column(BIGINT(unsigned=True), server_default=text("0")) + disk_write = db.Column(BIGINT(unsigned=True), server_default=text("0")) + ts = db.Column(INTEGER(unsigned=True), server_default=text("0")) __table_args__ = ( db.PrimaryKeyConstraint("id"), db.Index("idx_ct", create_time.desc()), diff --git a/src/flask_state/services/host_status.py b/src/flask_state/services/host_status.py index 83ef994..79098d7 100644 --- a/src/flask_state/services/host_status.py +++ b/src/flask_state/services/host_status.py @@ -11,7 +11,7 @@ retrieve_host_status, retrieve_host_status_yesterday, retrieve_latest_host_status, -) + create_host_io, delete_thirty_days_io_status, retrieve_latest_io_status) from ..exceptions import FlaskStateError, FlaskStateResponse, SuccessResponse from ..exceptions.error_code import MsgCode from ..exceptions.log_msg import ErrorMsg @@ -19,6 +19,7 @@ from ..utils.date import get_current_ms, get_current_s, get_formatted_timestamp from ..utils.logger import logger from . import redis_conn +import math def record_flask_state_host(interval, target_time): @@ -105,7 +106,7 @@ def query_redis_info(): yesterday_keyspace_misses = yesterday_current_statistic.keyspace_misses if yesterday_keyspace_hits is not None and yesterday_keyspace_misses is not None: be_divided_num = ( - keyspace_hits + keyspace_misses - (yesterday_keyspace_hits + yesterday_keyspace_misses) + keyspace_hits + keyspace_misses - (yesterday_keyspace_hits + yesterday_keyspace_misses) ) delta_hits_ratio = ( float( @@ -146,13 +147,13 @@ def record_flask_state_io_host(interval, target_time): host_io_status = query_host_io_info() result_conf.update(host_io_status) - create_host_status(result_conf) + create_host_io(result_conf) now_time = get_current_s() new_day_utc = ( datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=timezone.utc).timestamp() ) if now_time <= new_day_utc + interval: - delete_thirty_days_status() + delete_thirty_days_io_status() except Exception as e: logger.exception(e) @@ -173,6 +174,7 @@ def query_host_io_info(): "net_recv": net_recv, "disk_read": disk_read, "disk_write": disk_write, + "ts": get_current_ms(), } return result @@ -191,8 +193,21 @@ def query_flask_state_host(days) -> FlaskStateResponse: if days not in TimeConstants.DAYS_SCOPE: raise FlaskStateError(**MsgCode.OVERSTEP_DAYS_SCOPE.value, status_code=HTTPStatus.BAD_REQUEST) try: + io_info = {} + now_ts = get_current_ms() + now_io = query_host_io_info() + latest_io = retrieve_latest_io_status() + interval = math.ceil((now_ts - latest_io.get("ts")) / 1000) + if latest_io: + io_info.update({ + "net_sent": (now_io.get("net_sent") - latest_io.get("net_sent")) / interval, + "net_recv": (now_io.get("net_recv") - latest_io.get("net_recv")) / interval, + "disk_read": (now_io.get("disk_read") - latest_io.get("disk_read")) / interval, + "disk_write": (now_io.get("disk_write") - latest_io.get("disk_write")) / interval, + }) current_status = query_host_info() current_status.update(query_redis_info()) + current_status.update(io_info) current_status["load_avg"] = (current_status.get("load_avg") or "").split(",") except: current_status = retrieve_latest_host_status() diff --git a/src/flask_state/utils/file_lock.py b/src/flask_state/utils/file_lock.py index ee64161..26f6795 100644 --- a/src/flask_state/utils/file_lock.py +++ b/src/flask_state/utils/file_lock.py @@ -14,13 +14,13 @@ class Lock: @staticmethod - def get_file_lock(): - return FileLock() + def get_file_lock(io=""): + return FileLock(io) class FileLock: - def __init__(self): - lock_file = "821e9dab54fec92e3d054b3367a50b70d328caed" + def __init__(self, type): + lock_file = "821e9dab54fec92e3d054b3367a50b70d328caed_{type}".format(type=type) if SYSTEM == OperatingSystem.WINDOWS_SYSTEM: lock_dir = os.environ["tmp"] else: From bf9284811391fe569cc95a1a78bd04fd6bd36ec1 Mon Sep 17 00:00:00 2001 From: aaajiulong <525763728@qq.com> Date: Tue, 16 Feb 2021 15:33:41 +0800 Subject: [PATCH 08/20] feature: Get network IO sequence data --- src/flask_state/controller/manager.py | 15 +++-- src/flask_state/dao/host_status.py | 25 ++++++++ src/flask_state/services/host_status.py | 78 +++++++++++++++++++++---- 3 files changed, 102 insertions(+), 16 deletions(-) diff --git a/src/flask_state/controller/manager.py b/src/flask_state/controller/manager.py index 06dde31..2cf4559 100644 --- a/src/flask_state/controller/manager.py +++ b/src/flask_state/controller/manager.py @@ -12,7 +12,11 @@ from ..exceptions.log_msg import ErrorMsg, InfoMsg from ..models import model_init_app from ..services import redis_conn -from ..services.host_status import query_flask_state_host, record_flask_state_host, record_flask_state_io_host +from ..services.host_status import ( + query_flask_state_host, + record_flask_state_host, + record_flask_state_io_host, +) from ..utils.auth import auth_method, auth_user from ..utils.constants import HttpMethod, HTTPStatus from ..utils.file_lock import Lock @@ -57,7 +61,10 @@ def init_app(app, interval=60, log_instance=None): t_host.setDaemon(True) t_host.start() - t_io = threading.Thread(target=record_io_timer, args=(app, 10), ) + t_io = threading.Thread( + target=record_io_timer, + args=(app, 10), + ) t_io.setDaemon(True) t_io.start() @@ -107,10 +114,10 @@ def record_timer(app, interval): def record_io_timer(app, interval): - app.lock_flask_state = Lock.get_file_lock("io") + app.io_lock_flask_state = Lock.get_file_lock("io") with app.app_context(): try: - current_app.lock_flask_state.acquire() + current_app.io_lock_flask_state.acquire() s = sched.scheduler(time.time, time.sleep) in_time = time.time() diff --git a/src/flask_state/dao/host_status.py b/src/flask_state/dao/host_status.py index 3314993..dc450d4 100644 --- a/src/flask_state/dao/host_status.py +++ b/src/flask_state/dao/host_status.py @@ -31,6 +31,27 @@ def retrieve_host_status(days) -> list: return result +def retrieve_io_status(days) -> list: + """ + Query the status within the time period and flashback + + """ + target_time = get_current_ms() - get_query_ms(days) + result = ( + FlaskStateHost.query.with_entities( + FlaskStateIO.disk_read, + FlaskStateIO.disk_write, + FlaskStateIO.net_recv, + FlaskStateIO.net_sent, + FlaskStateIO.ts, + ) + .filter(FlaskStateIO.ts > target_time) + .order_by(FlaskStateIO.ts.desc()) + .all() + ) + return result + + def retrieve_latest_host_status() -> dict: """ Query the latest status @@ -40,6 +61,7 @@ def retrieve_latest_host_status() -> dict: result = result._asdict() if result else {} return result + def retrieve_latest_io_status() -> dict: """ Query the latest io status @@ -49,6 +71,7 @@ def retrieve_latest_io_status() -> dict: result = result._asdict() if result else {} return result + def create_host_status(kwargs): """ Create a new record @@ -91,6 +114,7 @@ def delete_thirty_days_status(): db.session.rollback() raise e + def delete_thirty_days_io_status(): """ Delete thirty days io records ago @@ -106,6 +130,7 @@ def delete_thirty_days_io_status(): db.session.rollback() raise e + def retrieve_host_status_yesterday() -> FlaskStateHost: """ Returns the closest time status between yesterday and the current diff --git a/src/flask_state/services/host_status.py b/src/flask_state/services/host_status.py index 79098d7..2131aeb 100644 --- a/src/flask_state/services/host_status.py +++ b/src/flask_state/services/host_status.py @@ -1,17 +1,23 @@ +import math import os import platform +from collections import namedtuple from datetime import datetime, timezone import psutil from ..conf.config import Config from ..dao.host_status import ( + create_host_io, create_host_status, + delete_thirty_days_io_status, delete_thirty_days_status, retrieve_host_status, retrieve_host_status_yesterday, + retrieve_io_status, retrieve_latest_host_status, - create_host_io, delete_thirty_days_io_status, retrieve_latest_io_status) + retrieve_latest_io_status, +) from ..exceptions import FlaskStateError, FlaskStateResponse, SuccessResponse from ..exceptions.error_code import MsgCode from ..exceptions.log_msg import ErrorMsg @@ -19,7 +25,6 @@ from ..utils.date import get_current_ms, get_current_s, get_formatted_timestamp from ..utils.logger import logger from . import redis_conn -import math def record_flask_state_host(interval, target_time): @@ -106,7 +111,7 @@ def query_redis_info(): yesterday_keyspace_misses = yesterday_current_statistic.keyspace_misses if yesterday_keyspace_hits is not None and yesterday_keyspace_misses is not None: be_divided_num = ( - keyspace_hits + keyspace_misses - (yesterday_keyspace_hits + yesterday_keyspace_misses) + keyspace_hits + keyspace_misses - (yesterday_keyspace_hits + yesterday_keyspace_misses) ) delta_hits_ratio = ( float( @@ -197,14 +202,25 @@ def query_flask_state_host(days) -> FlaskStateResponse: now_ts = get_current_ms() now_io = query_host_io_info() latest_io = retrieve_latest_io_status() - interval = math.ceil((now_ts - latest_io.get("ts")) / 1000) - if latest_io: - io_info.update({ - "net_sent": (now_io.get("net_sent") - latest_io.get("net_sent")) / interval, - "net_recv": (now_io.get("net_recv") - latest_io.get("net_recv")) / interval, - "disk_read": (now_io.get("disk_read") - latest_io.get("disk_read")) / interval, - "disk_write": (now_io.get("disk_write") - latest_io.get("disk_write")) / interval, - }) + if latest_io and math.ceil((now_ts - latest_io.get("ts")) / 1000) <= 60: + interval = math.ceil((now_ts - latest_io.get("ts")) / 1000) + io_info.update( + { + "net_sent": (now_io.get("net_sent") - latest_io.get("net_sent")) / interval, + "net_recv": (now_io.get("net_recv") - latest_io.get("net_recv")) / interval, + "disk_read": (now_io.get("disk_read") - latest_io.get("disk_read")) / interval, + "disk_write": (now_io.get("disk_write") - latest_io.get("disk_write")) / interval, + } + ) + else: + io_info.update( + { + "net_sent": 0, + "net_recv": 0, + "disk_read": 0, + "disk_write": 0, + } + ) current_status = query_host_info() current_status.update(query_redis_info()) current_status.update(io_info) @@ -214,7 +230,10 @@ def query_flask_state_host(days) -> FlaskStateResponse: current_status["load_avg"] = (current_status.get("load_avg") or "").split(",") result = retrieve_host_status(days) result = control_result_counts(result) + io_result = retrieve_io_status(days) + io_result = control_io_counts(io_result) arr = [] + io_arr = [] for status in result: arr.append( [ @@ -225,7 +244,15 @@ def query_flask_state_host(days) -> FlaskStateResponse: status.disk_usage, ] ) - data = {"currentStatistic": current_status, "items": arr} + for io_state in io_result: + io_arr.append( + [ + int(io_state.ts / TimeConstants.SECONDS_TO_MILLISECOND_MULTIPLE), + io_state.net_recv, + io_state.net_sent, + ] + ) + data = {"currentStatistic": current_status, "items": arr, "io": io_arr} return SuccessResponse(msg="Search success", data=data) @@ -247,6 +274,33 @@ def control_result_counts(result) -> list: return result +def control_io_counts(result) -> list: + result_length = len(result) + io_tuple = namedtuple("io", "net_recv, net_sent, ts") + if result_length > Config.MAX_RETURN_RECORDS: + refine_result = [] + interval = round(result_length / Config.MAX_RETURN_RECORDS, 2) + index = 0 + while index < result_length - 1 and len(refine_result) < Config.MAX_RETURN_RECORDS: + new_tmp = result[int(index)] + old_tmp = result[int(index + 1)] + now_item = io_tuple(new_tmp.net_recv - old_tmp.net_recv, new_tmp.net_sent - old_tmp.net_sent, new_tmp.ts) + refine_result.append(now_item) + index += interval + result = refine_result + else: + refine_result = [] + for index in range(result_length - 1): + now_item = io_tuple( + result[index].net_recv - result[index + 1].net_recv, + result[index].net_sent - result[index + 1].net_sent, + result[index].ts, + ) + refine_result.append(now_item) + result = refine_result + return result + + def row2dict(field): """ Model class to dictionary class From 6f0b4c34de52f7d21a7bd73eb7bf6447b16e5a0c Mon Sep 17 00:00:00 2001 From: aaajiulong <525763728@qq.com> Date: Tue, 16 Feb 2021 15:35:40 +0800 Subject: [PATCH 09/20] refactor: Compatible with network IO and disk IO data display --- examples/static/initial.js | 106 +++++++++++++++++++++++++++++++------ 1 file changed, 89 insertions(+), 17 deletions(-) diff --git a/examples/static/initial.js b/examples/static/initial.js index 60b872b..fe70dda 100644 --- a/examples/static/initial.js +++ b/examples/static/initial.js @@ -45,7 +45,7 @@ class MachineStatus { initFlaskStateContainer() { let _chart = this.mobile ? `
` : `
`; - let _content = `

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
` + _chart + `
`; + let _content = `

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Disk IO
Network IO
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
` + _chart + `
`; document.getElementsByTagName('body')[0].insertAdjacentHTML('beforeend', _content); } @@ -123,6 +123,8 @@ class MachineStatus { document.getElementById('fs-memory').innerHTML = this.language.memory; document.getElementById('fs-disk-usage').innerHTML = this.language.disk_usage; document.getElementById('fs-load-avg').innerHTML = this.language.load_avg; + document.getElementById('fs-disk-io').innerHTML = this.language.disk_io; + document.getElementById('fs-network-io').innerHTML = this.language.network_io; document.getElementById('fs-boot-seconds').innerHTML = this.language.boot_seconds; document.getElementById('fs-used-memory').innerHTML = this.language.used_memory; document.getElementById('fs-used-memory-rss').innerHTML = this.language.used_memory_rss; @@ -143,7 +145,7 @@ class MachineStatus { this.consoleDiskUsageChart = echarts.init(document.getElementById('fs-info-diskusage-chart'), null, {renderer: 'svg'}); this.cpuOption = MachineStatus.generateChatOption(this.mobile, this.language.cpu || 'CPU', '', this.language.today || 'Today'); this.memoryOption = MachineStatus.generateChatOption(this.mobile, this.language.memory || 'Memory', '', this.language.today || 'Today'); - this.diskUsageOption = MachineStatus.generateChatOption(this.mobile, this.language.disk_usage || 'Disk Usage', '', this.language.today || 'Today'); + this.diskUsageOption = MachineStatus.generateChatOption(this.mobile, this.language.network_io || 'Network IO', 'networkIO', this.language.today || 'Today'); this.loadavgOption = MachineStatus.generateChatOption(this.mobile, 'Load Avg', 'loadavg', this.language.minutes || 'min'); } @@ -173,14 +175,6 @@ class MachineStatus { const fields = ["ts", "cpu", "memory", "load_avg", "disk_usage"]; const data = response.data; - data.items = data.items.map(item => { - let element = {}; - fields.forEach((field, index) => { - if (field === "ts") return element[field] = SECONDS_TO_MILLISECONDS * item[index]; - element[field] = item[index]; - }); - return element; - }); let currentStatistic = data.currentStatistic; if (Object.keys(currentStatistic).length) { let hostInfoSpan = document.getElementById('fs-host-status').getElementsByClassName('fs-badge-content'); @@ -188,8 +182,10 @@ class MachineStatus { hostInfoSpan[1].innerHTML = currentStatistic.cpu + '%'; hostInfoSpan[2].innerHTML = currentStatistic.disk_usage + '%'; hostInfoSpan[3].innerHTML = currentStatistic.load_avg[0] + "," + currentStatistic.load_avg[1] + "," + currentStatistic.load_avg[2]; + hostInfoSpan[4].innerHTML = "R" + MachineStatus.getFormatBit(currentStatistic.disk_read) + " | " + "W" + MachineStatus.getFormatBit(currentStatistic.disk_write); + hostInfoSpan[5].innerHTML = "⬇ " + MachineStatus.getFormatBit(currentStatistic.net_recv) + " | " + "⬆ " + MachineStatus.getFormatBit(currentStatistic.net_sent); - hostInfoSpan[4].innerHTML = MachineStatus.getFormatSeconds(currentStatistic.boot_seconds || 0, this.language.days, this.language.hours, this.language.minutes, this.language.seconds); + hostInfoSpan[6].innerHTML = MachineStatus.getFormatSeconds(currentStatistic.boot_seconds || 0, this.language.days, this.language.hours, this.language.minutes, this.language.seconds); const machineIndex = ['memory', 'cpu', 'disk_usage', 'load_avg']; machineIndex.forEach(function (item, index) { @@ -252,8 +248,18 @@ class MachineStatus { } } + data.items = data.items.map(item => { + let element = {}; + fields.forEach((field, index) => { + if (field === "ts") return element[field] = SECONDS_TO_MILLISECONDS * item[index]; + element[field] = item[index]; + }); + return element; + }); data.items.reverse(); + data.io.reverse(); let dataMap = MachineStatus.getChartsData(data.items); + let ioMap = MachineStatus.getIOChartsData(data.io); let tsList = dataMap.ts_list; let cpuList = dataMap.cpu_list; @@ -263,14 +269,19 @@ class MachineStatus { let loadavg15MinList = dataMap.load_avg_list[2]; let diskUsageList = dataMap.disk_usage_list; + this.diskUsageOption.xAxis.data = ioMap.ts_list; + this.diskUsageOption.series[0].data = ioMap.net_recv; + this.diskUsageOption.series[1].data = ioMap.net_sent; + this.consoleDiskUsageChart.setOption(this.diskUsageOption); + this.memoryOption.xAxis.data = tsList; this.cpuOption.xAxis.data = tsList; this.loadavgOption.xAxis.data = tsList; - this.diskUsageOption.xAxis.data = tsList; + // this.diskUsageOption.xAxis.data = tsList; this.memoryOption.series[0].data = memoryList; this.cpuOption.series[0].data = cpuList; - this.diskUsageOption.series[0].data = diskUsageList; + // this.diskUsageOption.series[0].data = diskUsageList; this.loadavgOption.series[0].data = loadavgList; this.loadavgOption.series[1].data = loadavg5MinList; this.loadavgOption.series[2].data = loadavg15MinList; @@ -278,7 +289,6 @@ class MachineStatus { this.consoleMemoryChart.setOption(this.memoryOption); this.consoleCpuChart.setOption(this.cpuOption); this.consoleLoadavgChart.setOption(this.loadavgOption); - this.consoleDiskUsageChart.setOption(this.diskUsageOption); MachineStatus.resizeChart([this.consoleMemoryChart, this.consoleCpuChart, this.consoleLoadavgChart, this.consoleDiskUsageChart]); }).then(() => { this.consoleMemoryChart.hideLoading(); @@ -332,7 +342,7 @@ class MachineStatus { /* Initialize echart */ static generateChatOption(isMobile, titleText, tableName = '', lineName = '') { let baseData = { - color: tableName === 'loadavg' ? ['#ffa726', '#42a5f5', '#66bb6a'] : ['#42a5f5'], + color: tableName === 'loadavg' ? ['#ffa726', '#42a5f5', '#66bb6a'] : tableName === 'networkIO' ? ['#ffa726', '#42a5f5'] : ['#42a5f5'], title: { show: !isMobile, text: titleText @@ -341,6 +351,12 @@ class MachineStatus { trigger: 'axis', formatter: (params) => { let value = echarts.format.formatTime('yyyy-MM-dd hh:mm:ss', new Date(parseInt(params[0].axisValue)), false) + '
'; + if (tableName === 'networkIO') { + for (let i = 0; i < params.length; i++) { + value += (params[i].marker + params[i].seriesName + ': ' + MachineStatus.getFormatBit(params[i].value) + '
') + } + return value; + } for (let i = 0; i < params.length; i++) { value += (params[i].marker + params[i].seriesName + ': ' + params[i].value + (tableName === 'loadavg' ? '' : '%') + '
') @@ -353,7 +369,7 @@ class MachineStatus { textStyle: { fontSize: 14 }, - show: titleText === 'Load Avg', + show: titleText === 'Load Avg' || tableName === "networkIO", }, grid: { left: '3%', @@ -382,7 +398,12 @@ class MachineStatus { yAxis: { type: 'value', axisLabel: { - formatter: (value) => value + (titleText === 'Load Avg' ? '' : '%'), + formatter: (value) => { + if (tableName === 'networkIO') { + return MachineStatus.getFormatBit(value); + } + return value + (titleText === 'Load Avg' ? '' : '%') + }, }, }, series: [ @@ -406,6 +427,18 @@ class MachineStatus { }); }); } + if (tableName === 'networkIO') { + baseData.legend.data = ['net_recv', 'net_sent']; + baseData.series = []; + baseData.legend.data.forEach((name) => { + baseData.series.push({ + name: name, + type: 'line', + symbol: 'none', + hoverAnimation: false + }); + }); + } return baseData; } @@ -435,6 +468,45 @@ class MachineStatus { }; }; + /* Get Echarts data */ + static getIOChartsData(rawData) { + let netRecvList = []; + let netSentList = []; + let tsList = []; + for (let i = 0; i < rawData.length; i++) { + let item = rawData[i]; + tsList.push(item[0] * SECONDS_TO_MILLISECONDS); + netRecvList.push(item[1]); + netSentList.push(item[2]); + } + return { + 'net_recv': netRecvList, 'net_sent': netSentList, 'ts_list': tsList + }; + }; + + /* Get format Bit */ + static getFormatBit(value) { + let suffix; + let b_value = value; + if (b_value >= 10e11) { + b_value = (b_value / 10e11).toFixed(2); + suffix = "tb/s"; + } else if (b_value >= 10e8) { + b_value = (b_value / 10e8).toFixed(2); + suffix = "gb/s"; + } else if (b_value >= 10e5) { + b_value = (b_value / 10e5).toFixed(2); + suffix = "mb/s"; + } else if (b_value >= 10e2) { + b_value = (b_value / 10e2).toFixed(2); + suffix = "kb/s"; + } else { + suffix = "b/s"; + b_value = b_value.toFixed(2); + } + return b_value + suffix; + }; + /* Get format time */ static getFormatSeconds(value, days = 'days', hours = 'hours', minutes = 'min', seconds = 'seconds') { let secondTime = parseInt(value); From d53c87b072bced5013b8d2d3f4f1fff79d54d715 Mon Sep 17 00:00:00 2001 From: aaajiulong <525763728@qq.com> Date: Tue, 16 Feb 2021 15:36:29 +0800 Subject: [PATCH 10/20] style: Update I18N --- packages/i18n.js | 20 +++++++++++++++++++- packages/umd/IN.js | 2 ++ packages/umd/en.js | 2 ++ packages/umd/es.js | 2 ++ packages/umd/fil.js | 2 ++ packages/umd/fr.js | 2 ++ packages/umd/hi.js | 2 ++ packages/umd/ja.js | 2 ++ packages/umd/vi.js | 2 ++ packages/umd/zh.js | 2 ++ 10 files changed, 37 insertions(+), 1 deletion(-) diff --git a/packages/i18n.js b/packages/i18n.js index b742c9a..17cf685 100644 --- a/packages/i18n.js +++ b/packages/i18n.js @@ -6,6 +6,8 @@ const en = { "memory": "Memory", "disk_usage": "Disk Usage", "load_avg": "Load Avg", + "disk_io": "Disk IO", + "network_io": "Network IO", "boot_seconds": "Uptime", "redis_status": "Redis Status", "used_memory": "Used Mem", @@ -30,6 +32,8 @@ const zh = { "memory": "内存", "disk_usage": "磁盘使用率", "load_avg": "Load Avg", + "disk_io": "磁盘IO", + "network_io": "网络IO", "boot_seconds": "启动时长", "redis_status": "Redis状态", "used_memory": "分配总内存", @@ -54,6 +58,8 @@ const ja = { "memory": "メモリ", "disk_usage": "ディスクの使用率", "load_avg": "平均読み込み時間", + "disk_io": "ディスクIO", + "network_io": "ネットワークIO", "boot_seconds": "起動時間の秒", "redis_status": "Redisの状態", "used_memory": "使用メモリ", @@ -78,6 +84,8 @@ const es = { "memory": "Memoria", "disk_usage": "Uso del disco", "load_avg": "Promedio de carga", + "disk_io": "E / S de disco", + "network_io": "E / S de red", "boot_seconds": "Tiempo de actividad", "redis_status": "Estado de Redis", "used_memory": "Memoria total", @@ -102,6 +110,8 @@ const IN = { "memory": "Penyimpanan", "disk_usage": "Penggunaan Disk", "load_avg": "Beban Rata", + "disk_io": "Disk IO", + "network_io": "Jaringan IO", "boot_seconds": "Uptime", "redis_status": "Status Redis", "used_memory": "Memori Total", @@ -126,6 +136,8 @@ const fr = { "memory": "Mémoire", "disk_usage": "Utilisation du disque", "load_avg": "Load Avg", + "disk_io": "Disque IO", + "network_io": "Réseau IO", "boot_seconds": "Temps de démarrage", "redis_status": "Statut Redis", "used_memory": "Mémoire totale", @@ -150,6 +162,8 @@ const fil = { "memory": "Memorya", "disk_usage": "Paggamit ng disk", "load_avg": "Load Avg", + "disk_io": "Disk IO", + "network_io": "Network IO", "boot_seconds": "Uptime", "redis_status": "Redis Katayuan", "used_memory": "Kabuuang memorya", @@ -174,6 +188,8 @@ const vi = { "memory": "Ký ức", "disk_usage": "Sử dụng đĩa", "load_avg": "Tải trung bình", + "disk_io": "IO đĩa", + "network_io": "Mạng IO", "boot_seconds": "Gian hoạt", "redis_status": "Trạng thái Redis", "used_memory": "Phân bổ bộ nhớ", @@ -198,6 +214,8 @@ const hi = { "memory": "राम", "disk_usage": "डिस्क उपयोग", "load_avg": "औसत भार", + "disk_io": "डिस्क IO", + "network_io": "नेटवर्क आईओ", "boot_seconds": "अपटाइम", "redis_status": "रेडिस राज्य", "used_memory": "कुल मेमोरी आवंटित", @@ -214,4 +232,4 @@ const hi = { "today": "आज" }; -export {zh, en, ja, es, IN, fr, fil}; \ No newline at end of file +export {zh, en, ja, es, IN, fr, fil, vi, hi}; \ No newline at end of file diff --git a/packages/umd/IN.js b/packages/umd/IN.js index 7ebb374..66e7d43 100644 --- a/packages/umd/IN.js +++ b/packages/umd/IN.js @@ -6,6 +6,8 @@ const IN = { "memory": "Penyimpanan", "disk_usage": "Penggunaan Disk", "load_avg": "Beban Rata", + "disk_io": "Disk IO", + "network_io": "Jaringan IO", "boot_seconds": "Uptime", "redis_status": "Status Redis", "used_memory": "Memori Total", diff --git a/packages/umd/en.js b/packages/umd/en.js index 9d86df7..00c4479 100644 --- a/packages/umd/en.js +++ b/packages/umd/en.js @@ -6,6 +6,8 @@ const en = { "memory": "Memory", "disk_usage": "Disk Usage", "load_avg": "Load Avg", + "disk_io": "Disk IO", + "network_io": "Network IO", "boot_seconds": "Uptime", "redis_status": "Redis Status", "used_memory": "Used Mem", diff --git a/packages/umd/es.js b/packages/umd/es.js index 5e80a25..0baa8fb 100644 --- a/packages/umd/es.js +++ b/packages/umd/es.js @@ -6,6 +6,8 @@ const es = { "memory": "Memoria", "disk_usage": "Uso del disco", "load_avg": "Promedio de carga", + "disk_io": "E / S de disco", + "network_io": "E / S de red", "boot_seconds": "Tiempo de actividad", "redis_status": "Estado de Redis", "used_memory": "Memoria total", diff --git a/packages/umd/fil.js b/packages/umd/fil.js index 44f070c..44f28f2 100644 --- a/packages/umd/fil.js +++ b/packages/umd/fil.js @@ -6,6 +6,8 @@ const fil = { "memory": "Memorya", "disk_usage": "Paggamit ng disk", "load_avg": "Load Avg", + "disk_io": "Disk IO", + "network_io": "Network IO", "boot_seconds": "Uptime", "redis_status": "Redis Katayuan", "used_memory": "Kabuuang memorya", diff --git a/packages/umd/fr.js b/packages/umd/fr.js index 3841c43..28c2a67 100644 --- a/packages/umd/fr.js +++ b/packages/umd/fr.js @@ -6,6 +6,8 @@ const fr = { "memory": "Mémoire", "disk_usage": "Utilisation du disque", "load_avg": "Load Avg", + "disk_io": "Disque IO", + "network_io": "Réseau IO", "boot_seconds": "Temps de démarrage", "redis_status": "Statut Redis", "used_memory": "Mémoire totale", diff --git a/packages/umd/hi.js b/packages/umd/hi.js index 8fa9e7f..ad5db07 100644 --- a/packages/umd/hi.js +++ b/packages/umd/hi.js @@ -6,6 +6,8 @@ const hi = { "memory": "राम", "disk_usage": "डिस्क उपयोग", "load_avg": "औसत भार", + "disk_io": "डिस्क IO", + "network_io": "नेटवर्क आईओ", "boot_seconds": "अपटाइम", "redis_status": "रेडिस राज्य", "used_memory": "कुल मेमोरी आवंटित", diff --git a/packages/umd/ja.js b/packages/umd/ja.js index c2cd8aa..2fcef69 100644 --- a/packages/umd/ja.js +++ b/packages/umd/ja.js @@ -6,6 +6,8 @@ const ja = { "memory": "メモリ", "disk_usage": "ディスクの使用率", "load_avg": "平均読み込み時間", + "disk_io": "ディスクIO", + "network_io": "ネットワークIO", "boot_seconds": "起動時間の秒", "redis_status": "Redisの状態", "used_memory": "使用メモリ", diff --git a/packages/umd/vi.js b/packages/umd/vi.js index e27c878..8eff1fa 100644 --- a/packages/umd/vi.js +++ b/packages/umd/vi.js @@ -6,6 +6,8 @@ const vi = { "memory": "Ký ức", "disk_usage": "Sử dụng đĩa", "load_avg": "Tải trung bình", + "disk_io": "IO đĩa", + "network_io": "Mạng IO", "boot_seconds": "Gian hoạt", "redis_status": "Trạng thái Redis", "used_memory": "Phân bổ bộ nhớ", diff --git a/packages/umd/zh.js b/packages/umd/zh.js index abc0498..7895c66 100644 --- a/packages/umd/zh.js +++ b/packages/umd/zh.js @@ -6,6 +6,8 @@ const zh = { "memory": "内存", "disk_usage": "磁盘使用率", "load_avg": "Load Avg", + "disk_io": "磁盘IO", + "network_io": "网络IO", "boot_seconds": "启动时长", "redis_status": "Redis状态", "used_memory": "分配总内存", From bff2b9593974bc30a13170f98858a9581667eb6e Mon Sep 17 00:00:00 2001 From: aaajiulong <525763728@qq.com> Date: Tue, 16 Feb 2021 15:49:58 +0800 Subject: [PATCH 11/20] refactor: Remove redundant code --- examples/static/initial.js | 28 ++++++++++--------------- src/flask_state/dao/host_status.py | 1 - src/flask_state/services/host_status.py | 1 - 3 files changed, 11 insertions(+), 19 deletions(-) diff --git a/examples/static/initial.js b/examples/static/initial.js index fe70dda..a679029 100644 --- a/examples/static/initial.js +++ b/examples/static/initial.js @@ -43,8 +43,8 @@ class MachineStatus { /* Insert window element */ initFlaskStateContainer() { - let _chart = this.mobile ? `
` - : `
`; + let _chart = this.mobile ? `
` + : `
`; let _content = `

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Disk IO
Network IO
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
` + _chart + `
`; document.getElementsByTagName('body')[0].insertAdjacentHTML('beforeend', _content); } @@ -94,7 +94,7 @@ class MachineStatus { const elemDict = { 0: 'fs-info-tab-memory', 1: 'fs-info-tab-cpu', - 2: 'fs-info-tab-disk-usage', + 2: 'fs-info-network-io', 3: 'fs-info-tab-loadavg' }; for (let item of liArr) { @@ -142,10 +142,10 @@ class MachineStatus { this.consoleCpuChart = echarts.init(document.getElementById('fs-info-cpu-chart'), null, {renderer: 'svg'}); this.consoleMemoryChart = echarts.init(document.getElementById('fs-info-memory-chart'), null, {renderer: 'svg'}); this.consoleLoadavgChart = echarts.init(document.getElementById('fs-info-loadavg-chart'), null, {renderer: 'svg'}); - this.consoleDiskUsageChart = echarts.init(document.getElementById('fs-info-diskusage-chart'), null, {renderer: 'svg'}); + this.consoleDiskUsageChart = echarts.init(document.getElementById('fs-info-networkio-chart'), null, {renderer: 'svg'}); this.cpuOption = MachineStatus.generateChatOption(this.mobile, this.language.cpu || 'CPU', '', this.language.today || 'Today'); this.memoryOption = MachineStatus.generateChatOption(this.mobile, this.language.memory || 'Memory', '', this.language.today || 'Today'); - this.diskUsageOption = MachineStatus.generateChatOption(this.mobile, this.language.network_io || 'Network IO', 'networkIO', this.language.today || 'Today'); + this.networkIOOption = MachineStatus.generateChatOption(this.mobile, this.language.network_io || 'Network IO', 'networkIO', this.language.today || 'Today'); this.loadavgOption = MachineStatus.generateChatOption(this.mobile, 'Load Avg', 'loadavg', this.language.minutes || 'min'); } @@ -172,7 +172,6 @@ class MachineStatus { return; } - const fields = ["ts", "cpu", "memory", "load_avg", "disk_usage"]; const data = response.data; let currentStatistic = data.currentStatistic; @@ -248,6 +247,7 @@ class MachineStatus { } } + const fields = ["ts", "cpu", "memory", "load_avg"]; data.items = data.items.map(item => { let element = {}; fields.forEach((field, index) => { @@ -267,21 +267,18 @@ class MachineStatus { let loadavgList = dataMap.load_avg_list[0]; let loadavg5MinList = dataMap.load_avg_list[1]; let loadavg15MinList = dataMap.load_avg_list[2]; - let diskUsageList = dataMap.disk_usage_list; - this.diskUsageOption.xAxis.data = ioMap.ts_list; - this.diskUsageOption.series[0].data = ioMap.net_recv; - this.diskUsageOption.series[1].data = ioMap.net_sent; - this.consoleDiskUsageChart.setOption(this.diskUsageOption); + this.networkIOOption.xAxis.data = ioMap.ts_list; + this.networkIOOption.series[0].data = ioMap.net_recv; + this.networkIOOption.series[1].data = ioMap.net_sent; + this.consoleDiskUsageChart.setOption(this.networkIOOption); this.memoryOption.xAxis.data = tsList; this.cpuOption.xAxis.data = tsList; this.loadavgOption.xAxis.data = tsList; - // this.diskUsageOption.xAxis.data = tsList; this.memoryOption.series[0].data = memoryList; this.cpuOption.series[0].data = cpuList; - // this.diskUsageOption.series[0].data = diskUsageList; this.loadavgOption.series[0].data = loadavgList; this.loadavgOption.series[1].data = loadavg5MinList; this.loadavgOption.series[2].data = loadavg15MinList; @@ -445,7 +442,6 @@ class MachineStatus { /* Get Echarts data */ static getChartsData(rawData) { let cpuList = []; - let diskUsageList = []; let loadAvgList = []; let loadAvg5minList = []; let loadAvg15minList = []; @@ -454,7 +450,6 @@ class MachineStatus { for (let i = 0; i < rawData.length; i++) { let item = rawData[i]; cpuList.push(item.cpu); - diskUsageList.push(item.disk_usage); loadAvgList.push(item.load_avg[0]); loadAvg5minList.push(item.load_avg[1]); loadAvg15minList.push(item.load_avg[2]); @@ -462,8 +457,7 @@ class MachineStatus { tsList.push(item.ts); } return { - 'cpu_list': cpuList, 'disk_usage_list': diskUsageList, - 'load_avg_list': [loadAvgList, loadAvg5minList, loadAvg15minList], + 'cpu_list': cpuList, 'load_avg_list': [loadAvgList, loadAvg5minList, loadAvg15minList], 'memory_list': memoryList, 'ts_list': tsList }; }; diff --git a/src/flask_state/dao/host_status.py b/src/flask_state/dao/host_status.py index dc450d4..fa36e18 100644 --- a/src/flask_state/dao/host_status.py +++ b/src/flask_state/dao/host_status.py @@ -21,7 +21,6 @@ def retrieve_host_status(days) -> list: FlaskStateHost.cpu, FlaskStateHost.memory, FlaskStateHost.load_avg, - FlaskStateHost.disk_usage, FlaskStateHost.ts, ) .filter(FlaskStateHost.ts > target_time) diff --git a/src/flask_state/services/host_status.py b/src/flask_state/services/host_status.py index 2131aeb..0d01e05 100644 --- a/src/flask_state/services/host_status.py +++ b/src/flask_state/services/host_status.py @@ -241,7 +241,6 @@ def query_flask_state_host(days) -> FlaskStateResponse: status.cpu, status.memory, status.load_avg.split(","), - status.disk_usage, ] ) for io_state in io_result: From 0df499eb697ae84efe9f7fdaf423c35fe8a04586 Mon Sep 17 00:00:00 2001 From: aaajiulong <525763728@qq.com> Date: Tue, 16 Feb 2021 15:51:17 +0800 Subject: [PATCH 12/20] chore: Update package file --- packages/cjs/flask-state.js | 120 +++++++++++++++++++++++++------- packages/cjs/flask-state.min.js | 2 +- packages/umd/flask-state.js | 120 +++++++++++++++++++++++++------- packages/umd/flask-state.min.js | 2 +- 4 files changed, 188 insertions(+), 56 deletions(-) diff --git a/packages/cjs/flask-state.js b/packages/cjs/flask-state.js index a3d952e..605906c 100644 --- a/packages/cjs/flask-state.js +++ b/packages/cjs/flask-state.js @@ -56,9 +56,9 @@ /* Insert window element */ initFlaskStateContainer() { - let _chart = this.mobile ? `
` - : `
`; - let _content = `

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
` + _chart + `
`; + let _chart = this.mobile ? `
` + : `
`; + let _content = `

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Disk IO
Network IO
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
` + _chart + `
`; document.getElementsByTagName('body')[0].insertAdjacentHTML('beforeend', _content); } @@ -107,7 +107,7 @@ const elemDict = { 0: 'fs-info-tab-memory', 1: 'fs-info-tab-cpu', - 2: 'fs-info-tab-disk-usage', + 2: 'fs-info-network-io', 3: 'fs-info-tab-loadavg' }; for (let item of liArr) { @@ -136,6 +136,8 @@ document.getElementById('fs-memory').innerHTML = this.language.memory; document.getElementById('fs-disk-usage').innerHTML = this.language.disk_usage; document.getElementById('fs-load-avg').innerHTML = this.language.load_avg; + document.getElementById('fs-disk-io').innerHTML = this.language.disk_io; + document.getElementById('fs-network-io').innerHTML = this.language.network_io; document.getElementById('fs-boot-seconds').innerHTML = this.language.boot_seconds; document.getElementById('fs-used-memory').innerHTML = this.language.used_memory; document.getElementById('fs-used-memory-rss').innerHTML = this.language.used_memory_rss; @@ -153,10 +155,10 @@ this.consoleCpuChart = echarts.init(document.getElementById('fs-info-cpu-chart'), null, {renderer: 'svg'}); this.consoleMemoryChart = echarts.init(document.getElementById('fs-info-memory-chart'), null, {renderer: 'svg'}); this.consoleLoadavgChart = echarts.init(document.getElementById('fs-info-loadavg-chart'), null, {renderer: 'svg'}); - this.consoleDiskUsageChart = echarts.init(document.getElementById('fs-info-diskusage-chart'), null, {renderer: 'svg'}); + this.consoleDiskUsageChart = echarts.init(document.getElementById('fs-info-networkio-chart'), null, {renderer: 'svg'}); this.cpuOption = MachineStatus.generateChatOption(this.mobile, this.language.cpu || 'CPU', '', this.language.today || 'Today'); this.memoryOption = MachineStatus.generateChatOption(this.mobile, this.language.memory || 'Memory', '', this.language.today || 'Today'); - this.diskUsageOption = MachineStatus.generateChatOption(this.mobile, this.language.disk_usage || 'Disk Usage', '', this.language.today || 'Today'); + this.networkIOOption = MachineStatus.generateChatOption(this.mobile, this.language.network_io || 'Network IO', 'networkIO', this.language.today || 'Today'); this.loadavgOption = MachineStatus.generateChatOption(this.mobile, 'Load Avg', 'loadavg', this.language.minutes || 'min'); } @@ -183,17 +185,8 @@ return; } - const fields = ["ts", "cpu", "memory", "load_avg", "disk_usage"]; const data = response.data; - data.items = data.items.map(item => { - let element = {}; - fields.forEach((field, index) => { - if (field === "ts") return element[field] = SECONDS_TO_MILLISECONDS * item[index]; - element[field] = item[index]; - }); - return element; - }); let currentStatistic = data.currentStatistic; if (Object.keys(currentStatistic).length) { let hostInfoSpan = document.getElementById('fs-host-status').getElementsByClassName('fs-badge-content'); @@ -201,8 +194,10 @@ hostInfoSpan[1].innerHTML = currentStatistic.cpu + '%'; hostInfoSpan[2].innerHTML = currentStatistic.disk_usage + '%'; hostInfoSpan[3].innerHTML = currentStatistic.load_avg[0] + "," + currentStatistic.load_avg[1] + "," + currentStatistic.load_avg[2]; + hostInfoSpan[4].innerHTML = "R" + MachineStatus.getFormatBit(currentStatistic.disk_read) + " | " + "W" + MachineStatus.getFormatBit(currentStatistic.disk_write); + hostInfoSpan[5].innerHTML = "⬇ " + MachineStatus.getFormatBit(currentStatistic.net_recv) + " | " + "⬆ " + MachineStatus.getFormatBit(currentStatistic.net_sent); - hostInfoSpan[4].innerHTML = MachineStatus.getFormatSeconds(currentStatistic.boot_seconds || 0, this.language.days, this.language.hours, this.language.minutes, this.language.seconds); + hostInfoSpan[6].innerHTML = MachineStatus.getFormatSeconds(currentStatistic.boot_seconds || 0, this.language.days, this.language.hours, this.language.minutes, this.language.seconds); const machineIndex = ['memory', 'cpu', 'disk_usage', 'load_avg']; machineIndex.forEach(function (item, index) { @@ -265,8 +260,19 @@ } } + const fields = ["ts", "cpu", "memory", "load_avg"]; + data.items = data.items.map(item => { + let element = {}; + fields.forEach((field, index) => { + if (field === "ts") return element[field] = SECONDS_TO_MILLISECONDS * item[index]; + element[field] = item[index]; + }); + return element; + }); data.items.reverse(); + data.io.reverse(); let dataMap = MachineStatus.getChartsData(data.items); + let ioMap = MachineStatus.getIOChartsData(data.io); let tsList = dataMap.ts_list; let cpuList = dataMap.cpu_list; @@ -274,16 +280,18 @@ let loadavgList = dataMap.load_avg_list[0]; let loadavg5MinList = dataMap.load_avg_list[1]; let loadavg15MinList = dataMap.load_avg_list[2]; - let diskUsageList = dataMap.disk_usage_list; + + this.networkIOOption.xAxis.data = ioMap.ts_list; + this.networkIOOption.series[0].data = ioMap.net_recv; + this.networkIOOption.series[1].data = ioMap.net_sent; + this.consoleDiskUsageChart.setOption(this.networkIOOption); this.memoryOption.xAxis.data = tsList; this.cpuOption.xAxis.data = tsList; this.loadavgOption.xAxis.data = tsList; - this.diskUsageOption.xAxis.data = tsList; this.memoryOption.series[0].data = memoryList; this.cpuOption.series[0].data = cpuList; - this.diskUsageOption.series[0].data = diskUsageList; this.loadavgOption.series[0].data = loadavgList; this.loadavgOption.series[1].data = loadavg5MinList; this.loadavgOption.series[2].data = loadavg15MinList; @@ -291,7 +299,6 @@ this.consoleMemoryChart.setOption(this.memoryOption); this.consoleCpuChart.setOption(this.cpuOption); this.consoleLoadavgChart.setOption(this.loadavgOption); - this.consoleDiskUsageChart.setOption(this.diskUsageOption); MachineStatus.resizeChart([this.consoleMemoryChart, this.consoleCpuChart, this.consoleLoadavgChart, this.consoleDiskUsageChart]); }).then(() => { this.consoleMemoryChart.hideLoading(); @@ -345,7 +352,7 @@ /* Initialize echart */ static generateChatOption(isMobile, titleText, tableName = '', lineName = '') { let baseData = { - color: tableName === 'loadavg' ? ['#ffa726', '#42a5f5', '#66bb6a'] : ['#42a5f5'], + color: tableName === 'loadavg' ? ['#ffa726', '#42a5f5', '#66bb6a'] : tableName === 'networkIO' ? ['#ffa726', '#42a5f5'] : ['#42a5f5'], title: { show: !isMobile, text: titleText @@ -354,6 +361,12 @@ trigger: 'axis', formatter: (params) => { let value = echarts.format.formatTime('yyyy-MM-dd hh:mm:ss', new Date(parseInt(params[0].axisValue)), false) + '
'; + if (tableName === 'networkIO') { + for (let i = 0; i < params.length; i++) { + value += (params[i].marker + params[i].seriesName + ': ' + MachineStatus.getFormatBit(params[i].value) + '
') + } + return value; + } for (let i = 0; i < params.length; i++) { value += (params[i].marker + params[i].seriesName + ': ' + params[i].value + (tableName === 'loadavg' ? '' : '%') + '
') @@ -366,7 +379,7 @@ textStyle: { fontSize: 14 }, - show: titleText === 'Load Avg', + show: titleText === 'Load Avg' || tableName === "networkIO", }, grid: { left: '3%', @@ -395,7 +408,12 @@ yAxis: { type: 'value', axisLabel: { - formatter: (value) => value + (titleText === 'Load Avg' ? '' : '%'), + formatter: (value) => { + if (tableName === 'networkIO') { + return MachineStatus.getFormatBit(value); + } + return value + (titleText === 'Load Avg' ? '' : '%') + }, }, }, series: [ @@ -419,13 +437,24 @@ }); }); } + if (tableName === 'networkIO') { + baseData.legend.data = ['net_recv', 'net_sent']; + baseData.series = []; + baseData.legend.data.forEach((name) => { + baseData.series.push({ + name: name, + type: 'line', + symbol: 'none', + hoverAnimation: false + }); + }); + } return baseData; } /* Get Echarts data */ static getChartsData(rawData) { let cpuList = []; - let diskUsageList = []; let loadAvgList = []; let loadAvg5minList = []; let loadAvg15minList = []; @@ -434,7 +463,6 @@ for (let i = 0; i < rawData.length; i++) { let item = rawData[i]; cpuList.push(item.cpu); - diskUsageList.push(item.disk_usage); loadAvgList.push(item.load_avg[0]); loadAvg5minList.push(item.load_avg[1]); loadAvg15minList.push(item.load_avg[2]); @@ -442,12 +470,50 @@ tsList.push(item.ts); } return { - 'cpu_list': cpuList, 'disk_usage_list': diskUsageList, - 'load_avg_list': [loadAvgList, loadAvg5minList, loadAvg15minList], + 'cpu_list': cpuList, 'load_avg_list': [loadAvgList, loadAvg5minList, loadAvg15minList], 'memory_list': memoryList, 'ts_list': tsList }; }; + /* Get Echarts data */ + static getIOChartsData(rawData) { + let netRecvList = []; + let netSentList = []; + let tsList = []; + for (let i = 0; i < rawData.length; i++) { + let item = rawData[i]; + tsList.push(item[0] * SECONDS_TO_MILLISECONDS); + netRecvList.push(item[1]); + netSentList.push(item[2]); + } + return { + 'net_recv': netRecvList, 'net_sent': netSentList, 'ts_list': tsList + }; + }; + + /* Get format Bit */ + static getFormatBit(value) { + let suffix; + let b_value = value; + if (b_value >= 10e11) { + b_value = (b_value / 10e11).toFixed(2); + suffix = "tb/s"; + } else if (b_value >= 10e8) { + b_value = (b_value / 10e8).toFixed(2); + suffix = "gb/s"; + } else if (b_value >= 10e5) { + b_value = (b_value / 10e5).toFixed(2); + suffix = "mb/s"; + } else if (b_value >= 10e2) { + b_value = (b_value / 10e2).toFixed(2); + suffix = "kb/s"; + } else { + suffix = "b/s"; + b_value = b_value.toFixed(2); + } + return b_value + suffix; + }; + /* Get format time */ static getFormatSeconds(value, days = 'days', hours = 'hours', minutes = 'min', seconds = 'seconds') { let secondTime = parseInt(value); diff --git a/packages/cjs/flask-state.min.js b/packages/cjs/flask-state.min.js index 1b71336..97f1887 100644 --- a/packages/cjs/flask-state.min.js +++ b/packages/cjs/flask-state.min.js @@ -10,4 +10,4 @@ 'use strict'; -module.exports=function(e){var t={};function s(a){if(t[a])return t[a].exports;var n=t[a]={i:a,l:!1,exports:{}};return e[a].call(n.exports,n,n.exports,s),n.l=!0,n.exports}return s.m=e,s.c=t,s.d=function(e,t,a){s.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:a})},s.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},s.t=function(e,t){if(1&t&&(e=s(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var a=Object.create(null);if(s.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)s.d(a,n,function(t){return e[t]}.bind(null,n));return a},s.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return s.d(t,"a",t),t},s.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},s.p="",s(s.s=0)}([function(e,t,s){"use strict";const a=85,n=75,i=10,o=5,d=1048576;class r{constructor(e){this.language=e,this.mobile=r.isMobile(),this.index=0,this.initFlaskStateContainer(this.mobile),this.setEventListener(),this.initFlaskStateLanguage(this.language),this.setInitParams(),this.mobile&&this.setTagChangeEventListener(this.consoleCpuChart,this.consoleMemoryChart,this.consoleLoadavgChart,this.consoleDiskUsageChart),window.addEventListener("resize",(()=>{r.resizeChartTimer([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])}))}setFlaskStateData(){document.getElementById("fs-background").style.display="block",document.getElementById("fs-info-container").style.display="block",document.getElementsByTagName("body")[0].style.overflowX="hidden",document.getElementsByTagName("body")[0].style.overflowY="hidden",document.getElementById("fs-select-days").value="1",this.setCharts("1")}initFlaskStateContainer(){let e='

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
'+(this.mobile?'
':"
")+"
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e)}setEventListener(){if(window.addEventListener){document.getElementById("fs-info-close").addEventListener("click",(function(){document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out")})),document.getElementById("fs-background").addEventListener("click",(function(e){"fs-background"===String(e.target.id)&&(document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out"))}));let e=document.getElementById("fs-select-days");e.addEventListener("change",(()=>{this.setCharts(e.value)}))}}setTagChangeEventListener(...e){if(document.getElementById("fs-info-tab")){let t=document.getElementById("fs-info-tab").getElementsByTagName("li"),s=document.getElementById("fs-info-tab-memory"),a=t[0],n=0;const i={0:"fs-info-tab-memory",1:"fs-info-tab-cpu",2:"fs-info-tab-disk-usage",3:"fs-info-tab-loadavg"};for(let o of t){let t=document.getElementById(i[n]);o.children[0].addEventListener("click",(()=>{o.classList.add("active"),a.classList.remove("active"),s.classList.remove("fs-show"),t.classList.add("fs-show"),r.resizeChart(e),s=t,a=o})),n++}}}initFlaskStateLanguage(){0!==Object.keys(this.language).length&&(document.getElementById("fs-host-status-title").innerHTML=this.language.host_status,document.getElementById("fs-redis-status-title").innerHTML=this.language.redis_status,document.getElementById("fs-cpu").innerHTML=this.language.cpu,document.getElementById("fs-memory").innerHTML=this.language.memory,document.getElementById("fs-disk-usage").innerHTML=this.language.disk_usage,document.getElementById("fs-load-avg").innerHTML=this.language.load_avg,document.getElementById("fs-boot-seconds").innerHTML=this.language.boot_seconds,document.getElementById("fs-used-memory").innerHTML=this.language.used_memory,document.getElementById("fs-used-memory-rss").innerHTML=this.language.used_memory_rss,document.getElementById("fs-mem-fragmentation-ratio").innerHTML=this.language.mem_fragmentation_ratio,document.getElementById("fs-hits-ratio").innerHTML=this.language.hits_ratio,document.getElementById("fs-delta-hits-ratio").innerHTML=this.language.delta_hits_ratio,document.getElementById("fs-uptime-in-seconds").innerHTML=this.language.uptime_in_seconds,document.getElementById("fs-connected-clients").innerHTML=this.language.connected_clients,document.getElementById("fs-days").innerHTML=this.language.days)}setInitParams(){this.consoleCpuChart=echarts.init(document.getElementById("fs-info-cpu-chart"),null,{renderer:"svg"}),this.consoleMemoryChart=echarts.init(document.getElementById("fs-info-memory-chart"),null,{renderer:"svg"}),this.consoleLoadavgChart=echarts.init(document.getElementById("fs-info-loadavg-chart"),null,{renderer:"svg"}),this.consoleDiskUsageChart=echarts.init(document.getElementById("fs-info-diskusage-chart"),null,{renderer:"svg"}),this.cpuOption=r.generateChatOption(this.mobile,this.language.cpu||"CPU","",this.language.today||"Today"),this.memoryOption=r.generateChatOption(this.mobile,this.language.memory||"Memory","",this.language.today||"Today"),this.diskUsageOption=r.generateChatOption(this.mobile,this.language.disk_usage||"Disk Usage","",this.language.today||"Today"),this.loadavgOption=r.generateChatOption(this.mobile,"Load Avg","loadavg",this.language.minutes||"min")}setCharts(e){this.consoleCpuChart.showLoading(),this.consoleMemoryChart.showLoading(),this.consoleLoadavgChart.showLoading(),this.consoleDiskUsageChart.showLoading(),fetch("/v0/state/hoststatus",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json;charset=UTF-8"},body:JSON.stringify({timeQuantum:e})}).then((e=>{e.ok&&e.json().then((e=>{if(200!==e.code)return;const t=["ts","cpu","memory","load_avg","disk_usage"],s=e.data;s.items=s.items.map((e=>{let s={};return t.forEach(((t,a)=>{if("ts"===t)return s[t]=1e3*e[a];s[t]=e[a]})),s}));let l=s.currentStatistic;if(Object.keys(l).length){let e=document.getElementById("fs-host-status").getElementsByClassName("fs-badge-content");e[0].innerHTML=l.memory+"%",e[1].innerHTML=l.cpu+"%",e[2].innerHTML=l.disk_usage+"%",e[3].innerHTML=l.load_avg[0]+","+l.load_avg[1]+","+l.load_avg[2],e[4].innerHTML=r.getFormatSeconds(l.boot_seconds||0,this.language.days,this.language.hours,this.language.minutes,this.language.seconds);["memory","cpu","disk_usage","load_avg"].forEach((function(t,s){let d;if("load_avg"===t){let e=(l.load_avg[0]+l.load_avg[1]+l.load_avg[2])/3;d=e>=o?e>=i?"background-red":"background-orange":"background-green"}else d=l[t]>=n?l.memory>=a?"background-red":"background-orange":"background-green";let r=e[s].classList;r.remove("background-green","background-orange","background-red"),r.add(d)}));let t=document.getElementById("fs-redis-status").getElementsByClassName("fs-badge-content"),s=["used_memory","used_memory_rss","mem_fragmentation_ratio","hits_ratio","delta_hits_ratio","uptime_in_seconds","connected_clients"],c=!0;for(let e of s)if(l[e]){c=!1;break}c?(document.getElementById("fs-redis-status-title").innerHTML="",document.getElementById("fs-redis-status-title").style.marginTop="0",document.getElementById("fs-redis-status").style.display="none"):s.forEach(((e,s)=>{switch(e){case"used_memory":case"used_memory_rss":t[s].innerHTML=Math.ceil(l[e]/d)+" M";break;case"mem_fragmentation_ratio":let a=l[e];if(t[s].innerHTML=l[e],null!=a&&a>1){let e=t[s].classList;e.remove("background-green"),e.add("background-red")}break;case"hits_ratio":case"delta_hits_ratio":t[s].innerHTML=l[e]+"%";break;case"uptime_in_seconds":t[s].innerHTML=r.getFormatSeconds(l[e],this.language.days,this.language.hours,this.language.minutes,this.language.seconds);break;case"connected_clients":t[s].innerHTML=l[e]}}))}s.items.reverse();let c=r.getChartsData(s.items),g=c.ts_list,u=c.cpu_list,m=c.memory_list,h=c.load_avg_list[0],f=c.load_avg_list[1],p=c.load_avg_list[2],y=c.disk_usage_list;this.memoryOption.xAxis.data=g,this.cpuOption.xAxis.data=g,this.loadavgOption.xAxis.data=g,this.diskUsageOption.xAxis.data=g,this.memoryOption.series[0].data=m,this.cpuOption.series[0].data=u,this.diskUsageOption.series[0].data=y,this.loadavgOption.series[0].data=h,this.loadavgOption.series[1].data=f,this.loadavgOption.series[2].data=p,this.consoleMemoryChart.setOption(this.memoryOption),this.consoleCpuChart.setOption(this.cpuOption),this.consoleLoadavgChart.setOption(this.loadavgOption),this.consoleDiskUsageChart.setOption(this.diskUsageOption),r.resizeChart([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])})).then((()=>{this.consoleMemoryChart.hideLoading(),this.consoleCpuChart.hideLoading(),this.consoleLoadavgChart.hideLoading(),this.consoleDiskUsageChart.hideLoading()}))})).catch((function(e){}))}static isMobile(){let e=navigator.userAgent,t={trident:e.indexOf("Trident")>-1,presto:e.indexOf("Presto")>-1,webKit:e.indexOf("AppleWebKit")>-1,gecko:e.indexOf("Gecko")>-1&&-1===e.indexOf("KHTML"),mobile:e.match(/AppleWebKit.*Mobile.*/)&&!0,ios:e.match(/\(i[^;]+;( U;)? CPU.Mac OS X/)&&!0,android:e.indexOf("Android")>-1||e.indexOf("Linux")>-1,iPhone:e.indexOf("iPhone")>-1,iPad:e.indexOf("iPad")>-1,webApp:-1===e.indexOf("Safari"),wechat:e.indexOf("MicroMessenger")>-1,qq:e.match(/\sQQ/i)&&" qq"===e.match(/\sQQ/i)[0]};return t.iPhone||t.iPad||t.webApp||t.wechat||t.qq||t.ios||t.mobile||!1}static resizeChartTimer(e,t){clearTimeout(this.clearId),this.clearId=setTimeout((function(){r.resizeChart(e)}),t||200)}static resizeChart(e){e.forEach((e=>e.resize()))}static generateChatOption(e,t,s="",a=""){let n={color:"loadavg"===s?["#ffa726","#42a5f5","#66bb6a"]:["#42a5f5"],title:{show:!e,text:t},tooltip:{trigger:"axis",formatter:e=>{let t=echarts.format.formatTime("yyyy-MM-dd hh:mm:ss",new Date(parseInt(e[0].axisValue)),!1)+"
";for(let a=0;a";return t}},legend:{data:[a],textStyle:{fontSize:14},show:"Load Avg"===t},grid:{left:"3%",right:"4%",bottom:"3%",top:e?30:60,containLabel:!0},toolbox:{show:!e,feature:{saveAsImage:{title:" "}}},xAxis:{type:"category",boundaryGap:!1,axisLabel:{formatter:function(e){return echarts.format.formatTime("hh:mm",new Date(parseInt(e)),!1)}}},yAxis:{type:"value",axisLabel:{formatter:e=>e+("Load Avg"===t?"":"%")}},series:[{name:a,type:"line",symbol:"none",hoverAnimation:!1}]};return"loadavg"===s&&(n.legend.data=["1 "+a,"5 "+a,"15 "+a],n.series=[],n.legend.data.forEach((e=>{n.series.push({name:e,type:"line",symbol:"none",hoverAnimation:!1})}))),n}static getChartsData(e){let t=[],s=[],a=[],n=[],i=[],o=[],d=[];for(let r=0;r=60?(o=i/60,o>=60&&(d=o/60,o%=60),d>=24&&(r=d/24,d%=24)):l=i+n,o>0&&(l=Math.floor(o)+" "+a),d>0&&(l=Math.floor(d)+" "+s),r>0&&(l=Math.floor(r)+" "+t),l}}const l=function(){let e=null;return function(t){return e||(e=new r(t))}}();t.init=function(e){let t=null,s={};if(null!==e&&"object"==typeof e&&(t=e.hasOwnProperty("dom")?e.dom:null,s=e.hasOwnProperty("lang")&&e.lang.hasOwnProperty("language")?e.lang:{}),t instanceof HTMLElement){if(t.getAttribute("flaskState"))return;t.setAttribute("flaskState","true"),t.addEventListener("click",(()=>l(s).setFlaskStateData()))}else{if(document.getElementById("fs-state-circular"))return;let e="
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e);let t,n=document.getElementById("fs-state-circular");function a(e){n.style.top=e.clientY-t+300+"px"}n.onclick=function(){this.classList.add("fs-circular-out"),l(s).setFlaskStateData()},n.onmousedown=function(e){t=t||e.clientY,document.addEventListener("mousemove",a)},document.onmouseup=function(){document.removeEventListener("mousemove",a);const e=parseInt(n.style.top);n.classList.add("fs-circular-animation"),n.style.top=Math.min(Math.max(e,50),window.screen.height-200)+"px",setTimeout((()=>n.classList.remove("fs-circular-animation")),500)}}}}]); \ No newline at end of file +module.exports=function(e){var t={};function s(a){if(t[a])return t[a].exports;var n=t[a]={i:a,l:!1,exports:{}};return e[a].call(n.exports,n,n.exports,s),n.l=!0,n.exports}return s.m=e,s.c=t,s.d=function(e,t,a){s.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:a})},s.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},s.t=function(e,t){if(1&t&&(e=s(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var a=Object.create(null);if(s.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)s.d(a,n,function(t){return e[t]}.bind(null,n));return a},s.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return s.d(t,"a",t),t},s.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},s.p="",s(s.s=0)}([function(e,t,s){"use strict";const a=85,n=75,i=10,o=5,r=1048576;class d{constructor(e){this.language=e,this.mobile=d.isMobile(),this.index=0,this.initFlaskStateContainer(this.mobile),this.setEventListener(),this.initFlaskStateLanguage(this.language),this.setInitParams(),this.mobile&&this.setTagChangeEventListener(this.consoleCpuChart,this.consoleMemoryChart,this.consoleLoadavgChart,this.consoleDiskUsageChart),window.addEventListener("resize",(()=>{d.resizeChartTimer([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])}))}setFlaskStateData(){document.getElementById("fs-background").style.display="block",document.getElementById("fs-info-container").style.display="block",document.getElementsByTagName("body")[0].style.overflowX="hidden",document.getElementsByTagName("body")[0].style.overflowY="hidden",document.getElementById("fs-select-days").value="1",this.setCharts("1")}initFlaskStateContainer(){let e='

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Disk IO
Network IO
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
'+(this.mobile?'
':"
")+"
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e)}setEventListener(){if(window.addEventListener){document.getElementById("fs-info-close").addEventListener("click",(function(){document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out")})),document.getElementById("fs-background").addEventListener("click",(function(e){"fs-background"===String(e.target.id)&&(document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out"))}));let e=document.getElementById("fs-select-days");e.addEventListener("change",(()=>{this.setCharts(e.value)}))}}setTagChangeEventListener(...e){if(document.getElementById("fs-info-tab")){let t=document.getElementById("fs-info-tab").getElementsByTagName("li"),s=document.getElementById("fs-info-tab-memory"),a=t[0],n=0;const i={0:"fs-info-tab-memory",1:"fs-info-tab-cpu",2:"fs-info-network-io",3:"fs-info-tab-loadavg"};for(let o of t){let t=document.getElementById(i[n]);o.children[0].addEventListener("click",(()=>{o.classList.add("active"),a.classList.remove("active"),s.classList.remove("fs-show"),t.classList.add("fs-show"),d.resizeChart(e),s=t,a=o})),n++}}}initFlaskStateLanguage(){0!==Object.keys(this.language).length&&(document.getElementById("fs-host-status-title").innerHTML=this.language.host_status,document.getElementById("fs-redis-status-title").innerHTML=this.language.redis_status,document.getElementById("fs-cpu").innerHTML=this.language.cpu,document.getElementById("fs-memory").innerHTML=this.language.memory,document.getElementById("fs-disk-usage").innerHTML=this.language.disk_usage,document.getElementById("fs-load-avg").innerHTML=this.language.load_avg,document.getElementById("fs-disk-io").innerHTML=this.language.disk_io,document.getElementById("fs-network-io").innerHTML=this.language.network_io,document.getElementById("fs-boot-seconds").innerHTML=this.language.boot_seconds,document.getElementById("fs-used-memory").innerHTML=this.language.used_memory,document.getElementById("fs-used-memory-rss").innerHTML=this.language.used_memory_rss,document.getElementById("fs-mem-fragmentation-ratio").innerHTML=this.language.mem_fragmentation_ratio,document.getElementById("fs-hits-ratio").innerHTML=this.language.hits_ratio,document.getElementById("fs-delta-hits-ratio").innerHTML=this.language.delta_hits_ratio,document.getElementById("fs-uptime-in-seconds").innerHTML=this.language.uptime_in_seconds,document.getElementById("fs-connected-clients").innerHTML=this.language.connected_clients,document.getElementById("fs-days").innerHTML=this.language.days)}setInitParams(){this.consoleCpuChart=echarts.init(document.getElementById("fs-info-cpu-chart"),null,{renderer:"svg"}),this.consoleMemoryChart=echarts.init(document.getElementById("fs-info-memory-chart"),null,{renderer:"svg"}),this.consoleLoadavgChart=echarts.init(document.getElementById("fs-info-loadavg-chart"),null,{renderer:"svg"}),this.consoleDiskUsageChart=echarts.init(document.getElementById("fs-info-networkio-chart"),null,{renderer:"svg"}),this.cpuOption=d.generateChatOption(this.mobile,this.language.cpu||"CPU","",this.language.today||"Today"),this.memoryOption=d.generateChatOption(this.mobile,this.language.memory||"Memory","",this.language.today||"Today"),this.networkIOOption=d.generateChatOption(this.mobile,this.language.network_io||"Network IO","networkIO",this.language.today||"Today"),this.loadavgOption=d.generateChatOption(this.mobile,"Load Avg","loadavg",this.language.minutes||"min")}setCharts(e){this.consoleCpuChart.showLoading(),this.consoleMemoryChart.showLoading(),this.consoleLoadavgChart.showLoading(),this.consoleDiskUsageChart.showLoading(),fetch("/v0/state/hoststatus",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json;charset=UTF-8"},body:JSON.stringify({timeQuantum:e})}).then((e=>{e.ok&&e.json().then((e=>{if(200!==e.code)return;const t=e.data;let s=t.currentStatistic;if(Object.keys(s).length){let e=document.getElementById("fs-host-status").getElementsByClassName("fs-badge-content");e[0].innerHTML=s.memory+"%",e[1].innerHTML=s.cpu+"%",e[2].innerHTML=s.disk_usage+"%",e[3].innerHTML=s.load_avg[0]+","+s.load_avg[1]+","+s.load_avg[2],e[4].innerHTML="R"+d.getFormatBit(s.disk_read)+" | W"+d.getFormatBit(s.disk_write),e[5].innerHTML="⬇ "+d.getFormatBit(s.net_recv)+" | ⬆ "+d.getFormatBit(s.net_sent),e[6].innerHTML=d.getFormatSeconds(s.boot_seconds||0,this.language.days,this.language.hours,this.language.minutes,this.language.seconds);["memory","cpu","disk_usage","load_avg"].forEach((function(t,r){let d;if("load_avg"===t){let e=(s.load_avg[0]+s.load_avg[1]+s.load_avg[2])/3;d=e>=o?e>=i?"background-red":"background-orange":"background-green"}else d=s[t]>=n?s.memory>=a?"background-red":"background-orange":"background-green";let l=e[r].classList;l.remove("background-green","background-orange","background-red"),l.add(d)}));let t=document.getElementById("fs-redis-status").getElementsByClassName("fs-badge-content"),l=["used_memory","used_memory_rss","mem_fragmentation_ratio","hits_ratio","delta_hits_ratio","uptime_in_seconds","connected_clients"],c=!0;for(let e of l)if(s[e]){c=!1;break}c?(document.getElementById("fs-redis-status-title").innerHTML="",document.getElementById("fs-redis-status-title").style.marginTop="0",document.getElementById("fs-redis-status").style.display="none"):l.forEach(((e,a)=>{switch(e){case"used_memory":case"used_memory_rss":t[a].innerHTML=Math.ceil(s[e]/r)+" M";break;case"mem_fragmentation_ratio":let n=s[e];if(t[a].innerHTML=s[e],null!=n&&n>1){let e=t[a].classList;e.remove("background-green"),e.add("background-red")}break;case"hits_ratio":case"delta_hits_ratio":t[a].innerHTML=s[e]+"%";break;case"uptime_in_seconds":t[a].innerHTML=d.getFormatSeconds(s[e],this.language.days,this.language.hours,this.language.minutes,this.language.seconds);break;case"connected_clients":t[a].innerHTML=s[e]}}))}const l=["ts","cpu","memory","load_avg"];t.items=t.items.map((e=>{let t={};return l.forEach(((s,a)=>{if("ts"===s)return t[s]=1e3*e[a];t[s]=e[a]})),t})),t.items.reverse(),t.io.reverse();let c=d.getChartsData(t.items),g=d.getIOChartsData(t.io),u=c.ts_list,m=c.cpu_list,f=c.memory_list,h=c.load_avg_list[0],p=c.load_avg_list[1],y=c.load_avg_list[2];this.networkIOOption.xAxis.data=g.ts_list,this.networkIOOption.series[0].data=g.net_recv,this.networkIOOption.series[1].data=g.net_sent,this.consoleDiskUsageChart.setOption(this.networkIOOption),this.memoryOption.xAxis.data=u,this.cpuOption.xAxis.data=u,this.loadavgOption.xAxis.data=u,this.memoryOption.series[0].data=f,this.cpuOption.series[0].data=m,this.loadavgOption.series[0].data=h,this.loadavgOption.series[1].data=p,this.loadavgOption.series[2].data=y,this.consoleMemoryChart.setOption(this.memoryOption),this.consoleCpuChart.setOption(this.cpuOption),this.consoleLoadavgChart.setOption(this.loadavgOption),d.resizeChart([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])})).then((()=>{this.consoleMemoryChart.hideLoading(),this.consoleCpuChart.hideLoading(),this.consoleLoadavgChart.hideLoading(),this.consoleDiskUsageChart.hideLoading()}))})).catch((function(e){}))}static isMobile(){let e=navigator.userAgent,t={trident:e.indexOf("Trident")>-1,presto:e.indexOf("Presto")>-1,webKit:e.indexOf("AppleWebKit")>-1,gecko:e.indexOf("Gecko")>-1&&-1===e.indexOf("KHTML"),mobile:e.match(/AppleWebKit.*Mobile.*/)&&!0,ios:e.match(/\(i[^;]+;( U;)? CPU.Mac OS X/)&&!0,android:e.indexOf("Android")>-1||e.indexOf("Linux")>-1,iPhone:e.indexOf("iPhone")>-1,iPad:e.indexOf("iPad")>-1,webApp:-1===e.indexOf("Safari"),wechat:e.indexOf("MicroMessenger")>-1,qq:e.match(/\sQQ/i)&&" qq"===e.match(/\sQQ/i)[0]};return t.iPhone||t.iPad||t.webApp||t.wechat||t.qq||t.ios||t.mobile||!1}static resizeChartTimer(e,t){clearTimeout(this.clearId),this.clearId=setTimeout((function(){d.resizeChart(e)}),t||200)}static resizeChart(e){e.forEach((e=>e.resize()))}static generateChatOption(e,t,s="",a=""){let n={color:"loadavg"===s?["#ffa726","#42a5f5","#66bb6a"]:"networkIO"===s?["#ffa726","#42a5f5"]:["#42a5f5"],title:{show:!e,text:t},tooltip:{trigger:"axis",formatter:e=>{let t=echarts.format.formatTime("yyyy-MM-dd hh:mm:ss",new Date(parseInt(e[0].axisValue)),!1)+"
";if("networkIO"===s){for(let s=0;s";return t}for(let a=0;a";return t}},legend:{data:[a],textStyle:{fontSize:14},show:"Load Avg"===t||"networkIO"===s},grid:{left:"3%",right:"4%",bottom:"3%",top:e?30:60,containLabel:!0},toolbox:{show:!e,feature:{saveAsImage:{title:" "}}},xAxis:{type:"category",boundaryGap:!1,axisLabel:{formatter:function(e){return echarts.format.formatTime("hh:mm",new Date(parseInt(e)),!1)}}},yAxis:{type:"value",axisLabel:{formatter:e=>"networkIO"===s?d.getFormatBit(e):e+("Load Avg"===t?"":"%")}},series:[{name:a,type:"line",symbol:"none",hoverAnimation:!1}]};return"loadavg"===s&&(n.legend.data=["1 "+a,"5 "+a,"15 "+a],n.series=[],n.legend.data.forEach((e=>{n.series.push({name:e,type:"line",symbol:"none",hoverAnimation:!1})}))),"networkIO"===s&&(n.legend.data=["net_recv","net_sent"],n.series=[],n.legend.data.forEach((e=>{n.series.push({name:e,type:"line",symbol:"none",hoverAnimation:!1})}))),n}static getChartsData(e){let t=[],s=[],a=[],n=[],i=[],o=[];for(let r=0;r=1e12?(s=(s/1e12).toFixed(2),t="tb/s"):s>=1e9?(s=(s/1e9).toFixed(2),t="gb/s"):s>=1e6?(s=(s/1e6).toFixed(2),t="mb/s"):s>=1e3?(s=(s/1e3).toFixed(2),t="kb/s"):(t="b/s",s=s.toFixed(2)),s+t}static getFormatSeconds(e,t="days",s="hours",a="min",n="seconds"){let i=parseInt(e),o=0,r=0,d=0,l="";return i>=60?(o=i/60,o>=60&&(r=o/60,o%=60),r>=24&&(d=r/24,r%=24)):l=i+n,o>0&&(l=Math.floor(o)+" "+a),r>0&&(l=Math.floor(r)+" "+s),d>0&&(l=Math.floor(d)+" "+t),l}}const l=function(){let e=null;return function(t){return e||(e=new d(t))}}();t.init=function(e){let t=null,s={};if(null!==e&&"object"==typeof e&&(t=e.hasOwnProperty("dom")?e.dom:null,s=e.hasOwnProperty("lang")&&e.lang.hasOwnProperty("language")?e.lang:{}),t instanceof HTMLElement){if(t.getAttribute("flaskState"))return;t.setAttribute("flaskState","true"),t.addEventListener("click",(()=>l(s).setFlaskStateData()))}else{if(document.getElementById("fs-state-circular"))return;let e="
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e);let t,n=document.getElementById("fs-state-circular");function a(e){n.style.top=e.clientY-t+300+"px"}n.onclick=function(){this.classList.add("fs-circular-out"),l(s).setFlaskStateData()},n.onmousedown=function(e){t=t||e.clientY,document.addEventListener("mousemove",a)},document.onmouseup=function(){document.removeEventListener("mousemove",a);const e=parseInt(n.style.top);n.classList.add("fs-circular-animation"),n.style.top=Math.min(Math.max(e,50),window.screen.height-200)+"px",setTimeout((()=>n.classList.remove("fs-circular-animation")),500)}}}}]); \ No newline at end of file diff --git a/packages/umd/flask-state.js b/packages/umd/flask-state.js index 147b611..637281c 100644 --- a/packages/umd/flask-state.js +++ b/packages/umd/flask-state.js @@ -60,9 +60,9 @@ /* Insert window element */ initFlaskStateContainer() { - let _chart = this.mobile ? `
` - : `
`; - let _content = `

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
` + _chart + `
`; + let _chart = this.mobile ? `
` + : `
`; + let _content = `

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Disk IO
Network IO
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
` + _chart + `
`; document.getElementsByTagName('body')[0].insertAdjacentHTML('beforeend', _content); } @@ -111,7 +111,7 @@ const elemDict = { 0: 'fs-info-tab-memory', 1: 'fs-info-tab-cpu', - 2: 'fs-info-tab-disk-usage', + 2: 'fs-info-network-io', 3: 'fs-info-tab-loadavg' }; for (let item of liArr) { @@ -140,6 +140,8 @@ document.getElementById('fs-memory').innerHTML = this.language.memory; document.getElementById('fs-disk-usage').innerHTML = this.language.disk_usage; document.getElementById('fs-load-avg').innerHTML = this.language.load_avg; + document.getElementById('fs-disk-io').innerHTML = this.language.disk_io; + document.getElementById('fs-network-io').innerHTML = this.language.network_io; document.getElementById('fs-boot-seconds').innerHTML = this.language.boot_seconds; document.getElementById('fs-used-memory').innerHTML = this.language.used_memory; document.getElementById('fs-used-memory-rss').innerHTML = this.language.used_memory_rss; @@ -157,10 +159,10 @@ this.consoleCpuChart = echarts.init(document.getElementById('fs-info-cpu-chart'), null, {renderer: 'svg'}); this.consoleMemoryChart = echarts.init(document.getElementById('fs-info-memory-chart'), null, {renderer: 'svg'}); this.consoleLoadavgChart = echarts.init(document.getElementById('fs-info-loadavg-chart'), null, {renderer: 'svg'}); - this.consoleDiskUsageChart = echarts.init(document.getElementById('fs-info-diskusage-chart'), null, {renderer: 'svg'}); + this.consoleDiskUsageChart = echarts.init(document.getElementById('fs-info-networkio-chart'), null, {renderer: 'svg'}); this.cpuOption = MachineStatus.generateChatOption(this.mobile, this.language.cpu || 'CPU', '', this.language.today || 'Today'); this.memoryOption = MachineStatus.generateChatOption(this.mobile, this.language.memory || 'Memory', '', this.language.today || 'Today'); - this.diskUsageOption = MachineStatus.generateChatOption(this.mobile, this.language.disk_usage || 'Disk Usage', '', this.language.today || 'Today'); + this.networkIOOption = MachineStatus.generateChatOption(this.mobile, this.language.network_io || 'Network IO', 'networkIO', this.language.today || 'Today'); this.loadavgOption = MachineStatus.generateChatOption(this.mobile, 'Load Avg', 'loadavg', this.language.minutes || 'min'); } @@ -187,17 +189,8 @@ return; } - const fields = ["ts", "cpu", "memory", "load_avg", "disk_usage"]; const data = response.data; - data.items = data.items.map(item => { - let element = {}; - fields.forEach((field, index) => { - if (field === "ts") return element[field] = SECONDS_TO_MILLISECONDS * item[index]; - element[field] = item[index]; - }); - return element; - }); let currentStatistic = data.currentStatistic; if (Object.keys(currentStatistic).length) { let hostInfoSpan = document.getElementById('fs-host-status').getElementsByClassName('fs-badge-content'); @@ -205,8 +198,10 @@ hostInfoSpan[1].innerHTML = currentStatistic.cpu + '%'; hostInfoSpan[2].innerHTML = currentStatistic.disk_usage + '%'; hostInfoSpan[3].innerHTML = currentStatistic.load_avg[0] + "," + currentStatistic.load_avg[1] + "," + currentStatistic.load_avg[2]; + hostInfoSpan[4].innerHTML = "R" + MachineStatus.getFormatBit(currentStatistic.disk_read) + " | " + "W" + MachineStatus.getFormatBit(currentStatistic.disk_write); + hostInfoSpan[5].innerHTML = "⬇ " + MachineStatus.getFormatBit(currentStatistic.net_recv) + " | " + "⬆ " + MachineStatus.getFormatBit(currentStatistic.net_sent); - hostInfoSpan[4].innerHTML = MachineStatus.getFormatSeconds(currentStatistic.boot_seconds || 0, this.language.days, this.language.hours, this.language.minutes, this.language.seconds); + hostInfoSpan[6].innerHTML = MachineStatus.getFormatSeconds(currentStatistic.boot_seconds || 0, this.language.days, this.language.hours, this.language.minutes, this.language.seconds); const machineIndex = ['memory', 'cpu', 'disk_usage', 'load_avg']; machineIndex.forEach(function (item, index) { @@ -269,8 +264,19 @@ } } + const fields = ["ts", "cpu", "memory", "load_avg"]; + data.items = data.items.map(item => { + let element = {}; + fields.forEach((field, index) => { + if (field === "ts") return element[field] = SECONDS_TO_MILLISECONDS * item[index]; + element[field] = item[index]; + }); + return element; + }); data.items.reverse(); + data.io.reverse(); let dataMap = MachineStatus.getChartsData(data.items); + let ioMap = MachineStatus.getIOChartsData(data.io); let tsList = dataMap.ts_list; let cpuList = dataMap.cpu_list; @@ -278,16 +284,18 @@ let loadavgList = dataMap.load_avg_list[0]; let loadavg5MinList = dataMap.load_avg_list[1]; let loadavg15MinList = dataMap.load_avg_list[2]; - let diskUsageList = dataMap.disk_usage_list; + + this.networkIOOption.xAxis.data = ioMap.ts_list; + this.networkIOOption.series[0].data = ioMap.net_recv; + this.networkIOOption.series[1].data = ioMap.net_sent; + this.consoleDiskUsageChart.setOption(this.networkIOOption); this.memoryOption.xAxis.data = tsList; this.cpuOption.xAxis.data = tsList; this.loadavgOption.xAxis.data = tsList; - this.diskUsageOption.xAxis.data = tsList; this.memoryOption.series[0].data = memoryList; this.cpuOption.series[0].data = cpuList; - this.diskUsageOption.series[0].data = diskUsageList; this.loadavgOption.series[0].data = loadavgList; this.loadavgOption.series[1].data = loadavg5MinList; this.loadavgOption.series[2].data = loadavg15MinList; @@ -295,7 +303,6 @@ this.consoleMemoryChart.setOption(this.memoryOption); this.consoleCpuChart.setOption(this.cpuOption); this.consoleLoadavgChart.setOption(this.loadavgOption); - this.consoleDiskUsageChart.setOption(this.diskUsageOption); MachineStatus.resizeChart([this.consoleMemoryChart, this.consoleCpuChart, this.consoleLoadavgChart, this.consoleDiskUsageChart]); }).then(() => { this.consoleMemoryChart.hideLoading(); @@ -349,7 +356,7 @@ /* Initialize echart */ static generateChatOption(isMobile, titleText, tableName = '', lineName = '') { let baseData = { - color: tableName === 'loadavg' ? ['#ffa726', '#42a5f5', '#66bb6a'] : ['#42a5f5'], + color: tableName === 'loadavg' ? ['#ffa726', '#42a5f5', '#66bb6a'] : tableName === 'networkIO' ? ['#ffa726', '#42a5f5'] : ['#42a5f5'], title: { show: !isMobile, text: titleText @@ -358,6 +365,12 @@ trigger: 'axis', formatter: (params) => { let value = echarts.format.formatTime('yyyy-MM-dd hh:mm:ss', new Date(parseInt(params[0].axisValue)), false) + '
'; + if (tableName === 'networkIO') { + for (let i = 0; i < params.length; i++) { + value += (params[i].marker + params[i].seriesName + ': ' + MachineStatus.getFormatBit(params[i].value) + '
') + } + return value; + } for (let i = 0; i < params.length; i++) { value += (params[i].marker + params[i].seriesName + ': ' + params[i].value + (tableName === 'loadavg' ? '' : '%') + '
') @@ -370,7 +383,7 @@ textStyle: { fontSize: 14 }, - show: titleText === 'Load Avg', + show: titleText === 'Load Avg' || tableName === "networkIO", }, grid: { left: '3%', @@ -399,7 +412,12 @@ yAxis: { type: 'value', axisLabel: { - formatter: (value) => value + (titleText === 'Load Avg' ? '' : '%'), + formatter: (value) => { + if (tableName === 'networkIO') { + return MachineStatus.getFormatBit(value); + } + return value + (titleText === 'Load Avg' ? '' : '%') + }, }, }, series: [ @@ -423,13 +441,24 @@ }); }); } + if (tableName === 'networkIO') { + baseData.legend.data = ['net_recv', 'net_sent']; + baseData.series = []; + baseData.legend.data.forEach((name) => { + baseData.series.push({ + name: name, + type: 'line', + symbol: 'none', + hoverAnimation: false + }); + }); + } return baseData; } /* Get Echarts data */ static getChartsData(rawData) { let cpuList = []; - let diskUsageList = []; let loadAvgList = []; let loadAvg5minList = []; let loadAvg15minList = []; @@ -438,7 +467,6 @@ for (let i = 0; i < rawData.length; i++) { let item = rawData[i]; cpuList.push(item.cpu); - diskUsageList.push(item.disk_usage); loadAvgList.push(item.load_avg[0]); loadAvg5minList.push(item.load_avg[1]); loadAvg15minList.push(item.load_avg[2]); @@ -446,12 +474,50 @@ tsList.push(item.ts); } return { - 'cpu_list': cpuList, 'disk_usage_list': diskUsageList, - 'load_avg_list': [loadAvgList, loadAvg5minList, loadAvg15minList], + 'cpu_list': cpuList, 'load_avg_list': [loadAvgList, loadAvg5minList, loadAvg15minList], 'memory_list': memoryList, 'ts_list': tsList }; }; + /* Get Echarts data */ + static getIOChartsData(rawData) { + let netRecvList = []; + let netSentList = []; + let tsList = []; + for (let i = 0; i < rawData.length; i++) { + let item = rawData[i]; + tsList.push(item[0] * SECONDS_TO_MILLISECONDS); + netRecvList.push(item[1]); + netSentList.push(item[2]); + } + return { + 'net_recv': netRecvList, 'net_sent': netSentList, 'ts_list': tsList + }; + }; + + /* Get format Bit */ + static getFormatBit(value) { + let suffix; + let b_value = value; + if (b_value >= 10e11) { + b_value = (b_value / 10e11).toFixed(2); + suffix = "tb/s"; + } else if (b_value >= 10e8) { + b_value = (b_value / 10e8).toFixed(2); + suffix = "gb/s"; + } else if (b_value >= 10e5) { + b_value = (b_value / 10e5).toFixed(2); + suffix = "mb/s"; + } else if (b_value >= 10e2) { + b_value = (b_value / 10e2).toFixed(2); + suffix = "kb/s"; + } else { + suffix = "b/s"; + b_value = b_value.toFixed(2); + } + return b_value + suffix; + }; + /* Get format time */ static getFormatSeconds(value, days = 'days', hours = 'hours', minutes = 'min', seconds = 'seconds') { let secondTime = parseInt(value); diff --git a/packages/umd/flask-state.min.js b/packages/umd/flask-state.min.js index a0318ee..f5c5734 100644 --- a/packages/umd/flask-state.min.js +++ b/packages/umd/flask-state.min.js @@ -10,4 +10,4 @@ 'use strict'; -!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.flaskState=t():e.flaskState=t()}(window,(function(){return function(e){var t={};function s(a){if(t[a])return t[a].exports;var n=t[a]={i:a,l:!1,exports:{}};return e[a].call(n.exports,n,n.exports,s),n.l=!0,n.exports}return s.m=e,s.c=t,s.d=function(e,t,a){s.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:a})},s.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},s.t=function(e,t){if(1&t&&(e=s(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var a=Object.create(null);if(s.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)s.d(a,n,function(t){return e[t]}.bind(null,n));return a},s.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return s.d(t,"a",t),t},s.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},s.p="",s(s.s=0)}([function(e,t,s){"use strict";const a=85,n=75,i=10,o=5,d=1048576;class r{constructor(e){this.language=e,this.mobile=r.isMobile(),this.index=0,this.initFlaskStateContainer(this.mobile),this.setEventListener(),this.initFlaskStateLanguage(this.language),this.setInitParams(),this.mobile&&this.setTagChangeEventListener(this.consoleCpuChart,this.consoleMemoryChart,this.consoleLoadavgChart,this.consoleDiskUsageChart),window.addEventListener("resize",(()=>{r.resizeChartTimer([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])}))}setFlaskStateData(){document.getElementById("fs-background").style.display="block",document.getElementById("fs-info-container").style.display="block",document.getElementsByTagName("body")[0].style.overflowX="hidden",document.getElementsByTagName("body")[0].style.overflowY="hidden",document.getElementById("fs-select-days").value="1",this.setCharts("1")}initFlaskStateContainer(){let e='

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
'+(this.mobile?'
':"
")+"
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e)}setEventListener(){if(window.addEventListener){document.getElementById("fs-info-close").addEventListener("click",(function(){document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out")})),document.getElementById("fs-background").addEventListener("click",(function(e){"fs-background"===String(e.target.id)&&(document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out"))}));let e=document.getElementById("fs-select-days");e.addEventListener("change",(()=>{this.setCharts(e.value)}))}}setTagChangeEventListener(...e){if(document.getElementById("fs-info-tab")){let t=document.getElementById("fs-info-tab").getElementsByTagName("li"),s=document.getElementById("fs-info-tab-memory"),a=t[0],n=0;const i={0:"fs-info-tab-memory",1:"fs-info-tab-cpu",2:"fs-info-tab-disk-usage",3:"fs-info-tab-loadavg"};for(let o of t){let t=document.getElementById(i[n]);o.children[0].addEventListener("click",(()=>{o.classList.add("active"),a.classList.remove("active"),s.classList.remove("fs-show"),t.classList.add("fs-show"),r.resizeChart(e),s=t,a=o})),n++}}}initFlaskStateLanguage(){0!==Object.keys(this.language).length&&(document.getElementById("fs-host-status-title").innerHTML=this.language.host_status,document.getElementById("fs-redis-status-title").innerHTML=this.language.redis_status,document.getElementById("fs-cpu").innerHTML=this.language.cpu,document.getElementById("fs-memory").innerHTML=this.language.memory,document.getElementById("fs-disk-usage").innerHTML=this.language.disk_usage,document.getElementById("fs-load-avg").innerHTML=this.language.load_avg,document.getElementById("fs-boot-seconds").innerHTML=this.language.boot_seconds,document.getElementById("fs-used-memory").innerHTML=this.language.used_memory,document.getElementById("fs-used-memory-rss").innerHTML=this.language.used_memory_rss,document.getElementById("fs-mem-fragmentation-ratio").innerHTML=this.language.mem_fragmentation_ratio,document.getElementById("fs-hits-ratio").innerHTML=this.language.hits_ratio,document.getElementById("fs-delta-hits-ratio").innerHTML=this.language.delta_hits_ratio,document.getElementById("fs-uptime-in-seconds").innerHTML=this.language.uptime_in_seconds,document.getElementById("fs-connected-clients").innerHTML=this.language.connected_clients,document.getElementById("fs-days").innerHTML=this.language.days)}setInitParams(){this.consoleCpuChart=echarts.init(document.getElementById("fs-info-cpu-chart"),null,{renderer:"svg"}),this.consoleMemoryChart=echarts.init(document.getElementById("fs-info-memory-chart"),null,{renderer:"svg"}),this.consoleLoadavgChart=echarts.init(document.getElementById("fs-info-loadavg-chart"),null,{renderer:"svg"}),this.consoleDiskUsageChart=echarts.init(document.getElementById("fs-info-diskusage-chart"),null,{renderer:"svg"}),this.cpuOption=r.generateChatOption(this.mobile,this.language.cpu||"CPU","",this.language.today||"Today"),this.memoryOption=r.generateChatOption(this.mobile,this.language.memory||"Memory","",this.language.today||"Today"),this.diskUsageOption=r.generateChatOption(this.mobile,this.language.disk_usage||"Disk Usage","",this.language.today||"Today"),this.loadavgOption=r.generateChatOption(this.mobile,"Load Avg","loadavg",this.language.minutes||"min")}setCharts(e){this.consoleCpuChart.showLoading(),this.consoleMemoryChart.showLoading(),this.consoleLoadavgChart.showLoading(),this.consoleDiskUsageChart.showLoading(),fetch("/v0/state/hoststatus",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json;charset=UTF-8"},body:JSON.stringify({timeQuantum:e})}).then((e=>{e.ok&&e.json().then((e=>{if(200!==e.code)return;const t=["ts","cpu","memory","load_avg","disk_usage"],s=e.data;s.items=s.items.map((e=>{let s={};return t.forEach(((t,a)=>{if("ts"===t)return s[t]=1e3*e[a];s[t]=e[a]})),s}));let l=s.currentStatistic;if(Object.keys(l).length){let e=document.getElementById("fs-host-status").getElementsByClassName("fs-badge-content");e[0].innerHTML=l.memory+"%",e[1].innerHTML=l.cpu+"%",e[2].innerHTML=l.disk_usage+"%",e[3].innerHTML=l.load_avg[0]+","+l.load_avg[1]+","+l.load_avg[2],e[4].innerHTML=r.getFormatSeconds(l.boot_seconds||0,this.language.days,this.language.hours,this.language.minutes,this.language.seconds);["memory","cpu","disk_usage","load_avg"].forEach((function(t,s){let d;if("load_avg"===t){let e=(l.load_avg[0]+l.load_avg[1]+l.load_avg[2])/3;d=e>=o?e>=i?"background-red":"background-orange":"background-green"}else d=l[t]>=n?l.memory>=a?"background-red":"background-orange":"background-green";let r=e[s].classList;r.remove("background-green","background-orange","background-red"),r.add(d)}));let t=document.getElementById("fs-redis-status").getElementsByClassName("fs-badge-content"),s=["used_memory","used_memory_rss","mem_fragmentation_ratio","hits_ratio","delta_hits_ratio","uptime_in_seconds","connected_clients"],c=!0;for(let e of s)if(l[e]){c=!1;break}c?(document.getElementById("fs-redis-status-title").innerHTML="",document.getElementById("fs-redis-status-title").style.marginTop="0",document.getElementById("fs-redis-status").style.display="none"):s.forEach(((e,s)=>{switch(e){case"used_memory":case"used_memory_rss":t[s].innerHTML=Math.ceil(l[e]/d)+" M";break;case"mem_fragmentation_ratio":let a=l[e];if(t[s].innerHTML=l[e],null!=a&&a>1){let e=t[s].classList;e.remove("background-green"),e.add("background-red")}break;case"hits_ratio":case"delta_hits_ratio":t[s].innerHTML=l[e]+"%";break;case"uptime_in_seconds":t[s].innerHTML=r.getFormatSeconds(l[e],this.language.days,this.language.hours,this.language.minutes,this.language.seconds);break;case"connected_clients":t[s].innerHTML=l[e]}}))}s.items.reverse();let c=r.getChartsData(s.items),g=c.ts_list,u=c.cpu_list,m=c.memory_list,f=c.load_avg_list[0],h=c.load_avg_list[1],p=c.load_avg_list[2],y=c.disk_usage_list;this.memoryOption.xAxis.data=g,this.cpuOption.xAxis.data=g,this.loadavgOption.xAxis.data=g,this.diskUsageOption.xAxis.data=g,this.memoryOption.series[0].data=m,this.cpuOption.series[0].data=u,this.diskUsageOption.series[0].data=y,this.loadavgOption.series[0].data=f,this.loadavgOption.series[1].data=h,this.loadavgOption.series[2].data=p,this.consoleMemoryChart.setOption(this.memoryOption),this.consoleCpuChart.setOption(this.cpuOption),this.consoleLoadavgChart.setOption(this.loadavgOption),this.consoleDiskUsageChart.setOption(this.diskUsageOption),r.resizeChart([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])})).then((()=>{this.consoleMemoryChart.hideLoading(),this.consoleCpuChart.hideLoading(),this.consoleLoadavgChart.hideLoading(),this.consoleDiskUsageChart.hideLoading()}))})).catch((function(e){}))}static isMobile(){let e=navigator.userAgent,t={trident:e.indexOf("Trident")>-1,presto:e.indexOf("Presto")>-1,webKit:e.indexOf("AppleWebKit")>-1,gecko:e.indexOf("Gecko")>-1&&-1===e.indexOf("KHTML"),mobile:e.match(/AppleWebKit.*Mobile.*/)&&!0,ios:e.match(/\(i[^;]+;( U;)? CPU.Mac OS X/)&&!0,android:e.indexOf("Android")>-1||e.indexOf("Linux")>-1,iPhone:e.indexOf("iPhone")>-1,iPad:e.indexOf("iPad")>-1,webApp:-1===e.indexOf("Safari"),wechat:e.indexOf("MicroMessenger")>-1,qq:e.match(/\sQQ/i)&&" qq"===e.match(/\sQQ/i)[0]};return t.iPhone||t.iPad||t.webApp||t.wechat||t.qq||t.ios||t.mobile||!1}static resizeChartTimer(e,t){clearTimeout(this.clearId),this.clearId=setTimeout((function(){r.resizeChart(e)}),t||200)}static resizeChart(e){e.forEach((e=>e.resize()))}static generateChatOption(e,t,s="",a=""){let n={color:"loadavg"===s?["#ffa726","#42a5f5","#66bb6a"]:["#42a5f5"],title:{show:!e,text:t},tooltip:{trigger:"axis",formatter:e=>{let t=echarts.format.formatTime("yyyy-MM-dd hh:mm:ss",new Date(parseInt(e[0].axisValue)),!1)+"
";for(let a=0;a";return t}},legend:{data:[a],textStyle:{fontSize:14},show:"Load Avg"===t},grid:{left:"3%",right:"4%",bottom:"3%",top:e?30:60,containLabel:!0},toolbox:{show:!e,feature:{saveAsImage:{title:" "}}},xAxis:{type:"category",boundaryGap:!1,axisLabel:{formatter:function(e){return echarts.format.formatTime("hh:mm",new Date(parseInt(e)),!1)}}},yAxis:{type:"value",axisLabel:{formatter:e=>e+("Load Avg"===t?"":"%")}},series:[{name:a,type:"line",symbol:"none",hoverAnimation:!1}]};return"loadavg"===s&&(n.legend.data=["1 "+a,"5 "+a,"15 "+a],n.series=[],n.legend.data.forEach((e=>{n.series.push({name:e,type:"line",symbol:"none",hoverAnimation:!1})}))),n}static getChartsData(e){let t=[],s=[],a=[],n=[],i=[],o=[],d=[];for(let r=0;r=60?(o=i/60,o>=60&&(d=o/60,o%=60),d>=24&&(r=d/24,d%=24)):l=i+n,o>0&&(l=Math.floor(o)+" "+a),d>0&&(l=Math.floor(d)+" "+s),r>0&&(l=Math.floor(r)+" "+t),l}}const l=function(){let e=null;return function(t){return e||(e=new r(t))}}();t.init=function(e){let t=null,s={};if(null!==e&&"object"==typeof e&&(t=e.hasOwnProperty("dom")?e.dom:null,s=e.hasOwnProperty("lang")&&e.lang.hasOwnProperty("language")?e.lang:{}),t instanceof HTMLElement){if(t.getAttribute("flaskState"))return;t.setAttribute("flaskState","true"),t.addEventListener("click",(()=>l(s).setFlaskStateData()))}else{if(document.getElementById("fs-state-circular"))return;let e="
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e);let t,n=document.getElementById("fs-state-circular");function a(e){n.style.top=e.clientY-t+300+"px"}n.onclick=function(){this.classList.add("fs-circular-out"),l(s).setFlaskStateData()},n.onmousedown=function(e){t=t||e.clientY,document.addEventListener("mousemove",a)},document.onmouseup=function(){document.removeEventListener("mousemove",a);const e=parseInt(n.style.top);n.classList.add("fs-circular-animation"),n.style.top=Math.min(Math.max(e,50),window.screen.height-200)+"px",setTimeout((()=>n.classList.remove("fs-circular-animation")),500)}}}}])})); \ No newline at end of file +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.flaskState=t():e.flaskState=t()}(window,(function(){return function(e){var t={};function s(a){if(t[a])return t[a].exports;var n=t[a]={i:a,l:!1,exports:{}};return e[a].call(n.exports,n,n.exports,s),n.l=!0,n.exports}return s.m=e,s.c=t,s.d=function(e,t,a){s.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:a})},s.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},s.t=function(e,t){if(1&t&&(e=s(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var a=Object.create(null);if(s.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)s.d(a,n,function(t){return e[t]}.bind(null,n));return a},s.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return s.d(t,"a",t),t},s.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},s.p="",s(s.s=0)}([function(e,t,s){"use strict";const a=85,n=75,i=10,o=5,r=1048576;class d{constructor(e){this.language=e,this.mobile=d.isMobile(),this.index=0,this.initFlaskStateContainer(this.mobile),this.setEventListener(),this.initFlaskStateLanguage(this.language),this.setInitParams(),this.mobile&&this.setTagChangeEventListener(this.consoleCpuChart,this.consoleMemoryChart,this.consoleLoadavgChart,this.consoleDiskUsageChart),window.addEventListener("resize",(()=>{d.resizeChartTimer([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])}))}setFlaskStateData(){document.getElementById("fs-background").style.display="block",document.getElementById("fs-info-container").style.display="block",document.getElementsByTagName("body")[0].style.overflowX="hidden",document.getElementsByTagName("body")[0].style.overflowY="hidden",document.getElementById("fs-select-days").value="1",this.setCharts("1")}initFlaskStateContainer(){let e='

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Disk IO
Network IO
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
'+(this.mobile?'
':"
")+"
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e)}setEventListener(){if(window.addEventListener){document.getElementById("fs-info-close").addEventListener("click",(function(){document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out")})),document.getElementById("fs-background").addEventListener("click",(function(e){"fs-background"===String(e.target.id)&&(document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out"))}));let e=document.getElementById("fs-select-days");e.addEventListener("change",(()=>{this.setCharts(e.value)}))}}setTagChangeEventListener(...e){if(document.getElementById("fs-info-tab")){let t=document.getElementById("fs-info-tab").getElementsByTagName("li"),s=document.getElementById("fs-info-tab-memory"),a=t[0],n=0;const i={0:"fs-info-tab-memory",1:"fs-info-tab-cpu",2:"fs-info-network-io",3:"fs-info-tab-loadavg"};for(let o of t){let t=document.getElementById(i[n]);o.children[0].addEventListener("click",(()=>{o.classList.add("active"),a.classList.remove("active"),s.classList.remove("fs-show"),t.classList.add("fs-show"),d.resizeChart(e),s=t,a=o})),n++}}}initFlaskStateLanguage(){0!==Object.keys(this.language).length&&(document.getElementById("fs-host-status-title").innerHTML=this.language.host_status,document.getElementById("fs-redis-status-title").innerHTML=this.language.redis_status,document.getElementById("fs-cpu").innerHTML=this.language.cpu,document.getElementById("fs-memory").innerHTML=this.language.memory,document.getElementById("fs-disk-usage").innerHTML=this.language.disk_usage,document.getElementById("fs-load-avg").innerHTML=this.language.load_avg,document.getElementById("fs-disk-io").innerHTML=this.language.disk_io,document.getElementById("fs-network-io").innerHTML=this.language.network_io,document.getElementById("fs-boot-seconds").innerHTML=this.language.boot_seconds,document.getElementById("fs-used-memory").innerHTML=this.language.used_memory,document.getElementById("fs-used-memory-rss").innerHTML=this.language.used_memory_rss,document.getElementById("fs-mem-fragmentation-ratio").innerHTML=this.language.mem_fragmentation_ratio,document.getElementById("fs-hits-ratio").innerHTML=this.language.hits_ratio,document.getElementById("fs-delta-hits-ratio").innerHTML=this.language.delta_hits_ratio,document.getElementById("fs-uptime-in-seconds").innerHTML=this.language.uptime_in_seconds,document.getElementById("fs-connected-clients").innerHTML=this.language.connected_clients,document.getElementById("fs-days").innerHTML=this.language.days)}setInitParams(){this.consoleCpuChart=echarts.init(document.getElementById("fs-info-cpu-chart"),null,{renderer:"svg"}),this.consoleMemoryChart=echarts.init(document.getElementById("fs-info-memory-chart"),null,{renderer:"svg"}),this.consoleLoadavgChart=echarts.init(document.getElementById("fs-info-loadavg-chart"),null,{renderer:"svg"}),this.consoleDiskUsageChart=echarts.init(document.getElementById("fs-info-networkio-chart"),null,{renderer:"svg"}),this.cpuOption=d.generateChatOption(this.mobile,this.language.cpu||"CPU","",this.language.today||"Today"),this.memoryOption=d.generateChatOption(this.mobile,this.language.memory||"Memory","",this.language.today||"Today"),this.networkIOOption=d.generateChatOption(this.mobile,this.language.network_io||"Network IO","networkIO",this.language.today||"Today"),this.loadavgOption=d.generateChatOption(this.mobile,"Load Avg","loadavg",this.language.minutes||"min")}setCharts(e){this.consoleCpuChart.showLoading(),this.consoleMemoryChart.showLoading(),this.consoleLoadavgChart.showLoading(),this.consoleDiskUsageChart.showLoading(),fetch("/v0/state/hoststatus",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json;charset=UTF-8"},body:JSON.stringify({timeQuantum:e})}).then((e=>{e.ok&&e.json().then((e=>{if(200!==e.code)return;const t=e.data;let s=t.currentStatistic;if(Object.keys(s).length){let e=document.getElementById("fs-host-status").getElementsByClassName("fs-badge-content");e[0].innerHTML=s.memory+"%",e[1].innerHTML=s.cpu+"%",e[2].innerHTML=s.disk_usage+"%",e[3].innerHTML=s.load_avg[0]+","+s.load_avg[1]+","+s.load_avg[2],e[4].innerHTML="R"+d.getFormatBit(s.disk_read)+" | W"+d.getFormatBit(s.disk_write),e[5].innerHTML="⬇ "+d.getFormatBit(s.net_recv)+" | ⬆ "+d.getFormatBit(s.net_sent),e[6].innerHTML=d.getFormatSeconds(s.boot_seconds||0,this.language.days,this.language.hours,this.language.minutes,this.language.seconds);["memory","cpu","disk_usage","load_avg"].forEach((function(t,r){let d;if("load_avg"===t){let e=(s.load_avg[0]+s.load_avg[1]+s.load_avg[2])/3;d=e>=o?e>=i?"background-red":"background-orange":"background-green"}else d=s[t]>=n?s.memory>=a?"background-red":"background-orange":"background-green";let l=e[r].classList;l.remove("background-green","background-orange","background-red"),l.add(d)}));let t=document.getElementById("fs-redis-status").getElementsByClassName("fs-badge-content"),l=["used_memory","used_memory_rss","mem_fragmentation_ratio","hits_ratio","delta_hits_ratio","uptime_in_seconds","connected_clients"],c=!0;for(let e of l)if(s[e]){c=!1;break}c?(document.getElementById("fs-redis-status-title").innerHTML="",document.getElementById("fs-redis-status-title").style.marginTop="0",document.getElementById("fs-redis-status").style.display="none"):l.forEach(((e,a)=>{switch(e){case"used_memory":case"used_memory_rss":t[a].innerHTML=Math.ceil(s[e]/r)+" M";break;case"mem_fragmentation_ratio":let n=s[e];if(t[a].innerHTML=s[e],null!=n&&n>1){let e=t[a].classList;e.remove("background-green"),e.add("background-red")}break;case"hits_ratio":case"delta_hits_ratio":t[a].innerHTML=s[e]+"%";break;case"uptime_in_seconds":t[a].innerHTML=d.getFormatSeconds(s[e],this.language.days,this.language.hours,this.language.minutes,this.language.seconds);break;case"connected_clients":t[a].innerHTML=s[e]}}))}const l=["ts","cpu","memory","load_avg"];t.items=t.items.map((e=>{let t={};return l.forEach(((s,a)=>{if("ts"===s)return t[s]=1e3*e[a];t[s]=e[a]})),t})),t.items.reverse(),t.io.reverse();let c=d.getChartsData(t.items),g=d.getIOChartsData(t.io),u=c.ts_list,m=c.cpu_list,f=c.memory_list,h=c.load_avg_list[0],p=c.load_avg_list[1],y=c.load_avg_list[2];this.networkIOOption.xAxis.data=g.ts_list,this.networkIOOption.series[0].data=g.net_recv,this.networkIOOption.series[1].data=g.net_sent,this.consoleDiskUsageChart.setOption(this.networkIOOption),this.memoryOption.xAxis.data=u,this.cpuOption.xAxis.data=u,this.loadavgOption.xAxis.data=u,this.memoryOption.series[0].data=f,this.cpuOption.series[0].data=m,this.loadavgOption.series[0].data=h,this.loadavgOption.series[1].data=p,this.loadavgOption.series[2].data=y,this.consoleMemoryChart.setOption(this.memoryOption),this.consoleCpuChart.setOption(this.cpuOption),this.consoleLoadavgChart.setOption(this.loadavgOption),d.resizeChart([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])})).then((()=>{this.consoleMemoryChart.hideLoading(),this.consoleCpuChart.hideLoading(),this.consoleLoadavgChart.hideLoading(),this.consoleDiskUsageChart.hideLoading()}))})).catch((function(e){}))}static isMobile(){let e=navigator.userAgent,t={trident:e.indexOf("Trident")>-1,presto:e.indexOf("Presto")>-1,webKit:e.indexOf("AppleWebKit")>-1,gecko:e.indexOf("Gecko")>-1&&-1===e.indexOf("KHTML"),mobile:e.match(/AppleWebKit.*Mobile.*/)&&!0,ios:e.match(/\(i[^;]+;( U;)? CPU.Mac OS X/)&&!0,android:e.indexOf("Android")>-1||e.indexOf("Linux")>-1,iPhone:e.indexOf("iPhone")>-1,iPad:e.indexOf("iPad")>-1,webApp:-1===e.indexOf("Safari"),wechat:e.indexOf("MicroMessenger")>-1,qq:e.match(/\sQQ/i)&&" qq"===e.match(/\sQQ/i)[0]};return t.iPhone||t.iPad||t.webApp||t.wechat||t.qq||t.ios||t.mobile||!1}static resizeChartTimer(e,t){clearTimeout(this.clearId),this.clearId=setTimeout((function(){d.resizeChart(e)}),t||200)}static resizeChart(e){e.forEach((e=>e.resize()))}static generateChatOption(e,t,s="",a=""){let n={color:"loadavg"===s?["#ffa726","#42a5f5","#66bb6a"]:"networkIO"===s?["#ffa726","#42a5f5"]:["#42a5f5"],title:{show:!e,text:t},tooltip:{trigger:"axis",formatter:e=>{let t=echarts.format.formatTime("yyyy-MM-dd hh:mm:ss",new Date(parseInt(e[0].axisValue)),!1)+"
";if("networkIO"===s){for(let s=0;s";return t}for(let a=0;a";return t}},legend:{data:[a],textStyle:{fontSize:14},show:"Load Avg"===t||"networkIO"===s},grid:{left:"3%",right:"4%",bottom:"3%",top:e?30:60,containLabel:!0},toolbox:{show:!e,feature:{saveAsImage:{title:" "}}},xAxis:{type:"category",boundaryGap:!1,axisLabel:{formatter:function(e){return echarts.format.formatTime("hh:mm",new Date(parseInt(e)),!1)}}},yAxis:{type:"value",axisLabel:{formatter:e=>"networkIO"===s?d.getFormatBit(e):e+("Load Avg"===t?"":"%")}},series:[{name:a,type:"line",symbol:"none",hoverAnimation:!1}]};return"loadavg"===s&&(n.legend.data=["1 "+a,"5 "+a,"15 "+a],n.series=[],n.legend.data.forEach((e=>{n.series.push({name:e,type:"line",symbol:"none",hoverAnimation:!1})}))),"networkIO"===s&&(n.legend.data=["net_recv","net_sent"],n.series=[],n.legend.data.forEach((e=>{n.series.push({name:e,type:"line",symbol:"none",hoverAnimation:!1})}))),n}static getChartsData(e){let t=[],s=[],a=[],n=[],i=[],o=[];for(let r=0;r=1e12?(s=(s/1e12).toFixed(2),t="tb/s"):s>=1e9?(s=(s/1e9).toFixed(2),t="gb/s"):s>=1e6?(s=(s/1e6).toFixed(2),t="mb/s"):s>=1e3?(s=(s/1e3).toFixed(2),t="kb/s"):(t="b/s",s=s.toFixed(2)),s+t}static getFormatSeconds(e,t="days",s="hours",a="min",n="seconds"){let i=parseInt(e),o=0,r=0,d=0,l="";return i>=60?(o=i/60,o>=60&&(r=o/60,o%=60),r>=24&&(d=r/24,r%=24)):l=i+n,o>0&&(l=Math.floor(o)+" "+a),r>0&&(l=Math.floor(r)+" "+s),d>0&&(l=Math.floor(d)+" "+t),l}}const l=function(){let e=null;return function(t){return e||(e=new d(t))}}();t.init=function(e){let t=null,s={};if(null!==e&&"object"==typeof e&&(t=e.hasOwnProperty("dom")?e.dom:null,s=e.hasOwnProperty("lang")&&e.lang.hasOwnProperty("language")?e.lang:{}),t instanceof HTMLElement){if(t.getAttribute("flaskState"))return;t.setAttribute("flaskState","true"),t.addEventListener("click",(()=>l(s).setFlaskStateData()))}else{if(document.getElementById("fs-state-circular"))return;let e="
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e);let t,n=document.getElementById("fs-state-circular");function a(e){n.style.top=e.clientY-t+300+"px"}n.onclick=function(){this.classList.add("fs-circular-out"),l(s).setFlaskStateData()},n.onmousedown=function(e){t=t||e.clientY,document.addEventListener("mousemove",a)},document.onmouseup=function(){document.removeEventListener("mousemove",a);const e=parseInt(n.style.top);n.classList.add("fs-circular-animation"),n.style.top=Math.min(Math.max(e,50),window.screen.height-200)+"px",setTimeout((()=>n.classList.remove("fs-circular-animation")),500)}}}}])})); \ No newline at end of file From 409d4387851cfe0af7a9e945d165a36fdbf6cc32 Mon Sep 17 00:00:00 2001 From: aaajiulong <525763728@qq.com> Date: Tue, 16 Feb 2021 17:15:04 +0800 Subject: [PATCH 13/20] style: Update interface style --- examples/static/initial.js | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/examples/static/initial.js b/examples/static/initial.js index a679029..3d1f652 100644 --- a/examples/static/initial.js +++ b/examples/static/initial.js @@ -181,8 +181,8 @@ class MachineStatus { hostInfoSpan[1].innerHTML = currentStatistic.cpu + '%'; hostInfoSpan[2].innerHTML = currentStatistic.disk_usage + '%'; hostInfoSpan[3].innerHTML = currentStatistic.load_avg[0] + "," + currentStatistic.load_avg[1] + "," + currentStatistic.load_avg[2]; - hostInfoSpan[4].innerHTML = "R" + MachineStatus.getFormatBit(currentStatistic.disk_read) + " | " + "W" + MachineStatus.getFormatBit(currentStatistic.disk_write); - hostInfoSpan[5].innerHTML = "⬇ " + MachineStatus.getFormatBit(currentStatistic.net_recv) + " | " + "⬆ " + MachineStatus.getFormatBit(currentStatistic.net_sent); + hostInfoSpan[4].innerHTML = "R " + MachineStatus.getFormatBit(currentStatistic.disk_read) + " | " + "W " + MachineStatus.getFormatBit(currentStatistic.disk_write); + hostInfoSpan[5].innerHTML = "" + MachineStatus.getFormatBit(currentStatistic.net_recv) + " | " + "" + MachineStatus.getFormatBit(currentStatistic.net_sent); hostInfoSpan[6].innerHTML = MachineStatus.getFormatSeconds(currentStatistic.boot_seconds || 0, this.language.days, this.language.hours, this.language.minutes, this.language.seconds); @@ -397,7 +397,8 @@ class MachineStatus { axisLabel: { formatter: (value) => { if (tableName === 'networkIO') { - return MachineStatus.getFormatBit(value); + let result = MachineStatus.getFormatBit(value, 0); + return result.substring(0, result.length - 2); } return value + (titleText === 'Load Avg' ? '' : '%') }, @@ -425,7 +426,7 @@ class MachineStatus { }); } if (tableName === 'networkIO') { - baseData.legend.data = ['net_recv', 'net_sent']; + baseData.legend.data = ['recv', 'sent']; baseData.series = []; baseData.legend.data.forEach((name) => { baseData.series.push({ @@ -479,26 +480,26 @@ class MachineStatus { }; /* Get format Bit */ - static getFormatBit(value) { + static getFormatBit(value, fixed = 2) { let suffix; - let b_value = value; + let b_value = value / 8; if (b_value >= 10e11) { - b_value = (b_value / 10e11).toFixed(2); - suffix = "tb/s"; + b_value = (b_value / 10e11).toFixed(fixed); + suffix = "TB/s"; } else if (b_value >= 10e8) { - b_value = (b_value / 10e8).toFixed(2); - suffix = "gb/s"; + b_value = (b_value / 10e8).toFixed(fixed); + suffix = "GB/s"; } else if (b_value >= 10e5) { - b_value = (b_value / 10e5).toFixed(2); - suffix = "mb/s"; + b_value = (b_value / 10e5).toFixed(fixed); + suffix = "MB/s"; } else if (b_value >= 10e2) { - b_value = (b_value / 10e2).toFixed(2); - suffix = "kb/s"; + b_value = (b_value / 10e2).toFixed(fixed); + suffix = "KB/s"; } else { - suffix = "b/s"; + suffix = "B/s"; b_value = b_value.toFixed(2); } - return b_value + suffix; + return b_value + ' ' + suffix; }; /* Get format time */ From f07ce6a96a0947019db069c15c09e16a2e5652b3 Mon Sep 17 00:00:00 2001 From: aaajiulong <525763728@qq.com> Date: Tue, 16 Feb 2021 18:28:14 +0800 Subject: [PATCH 14/20] chore: Update package file --- examples/templates/index.html | 5 +++++ packages/cjs/flask-state.js | 33 +++++++++++++++++---------------- packages/cjs/flask-state.min.js | 2 +- packages/umd/flask-state.js | 33 +++++++++++++++++---------------- packages/umd/flask-state.min.js | 2 +- 5 files changed, 41 insertions(+), 34 deletions(-) diff --git a/examples/templates/index.html b/examples/templates/index.html index 2b28895..f460d6f 100644 --- a/examples/templates/index.html +++ b/examples/templates/index.html @@ -15,6 +15,11 @@ click me \ No newline at end of file diff --git a/packages/cjs/flask-state.js b/packages/cjs/flask-state.js index 605906c..b454722 100644 --- a/packages/cjs/flask-state.js +++ b/packages/cjs/flask-state.js @@ -194,8 +194,8 @@ hostInfoSpan[1].innerHTML = currentStatistic.cpu + '%'; hostInfoSpan[2].innerHTML = currentStatistic.disk_usage + '%'; hostInfoSpan[3].innerHTML = currentStatistic.load_avg[0] + "," + currentStatistic.load_avg[1] + "," + currentStatistic.load_avg[2]; - hostInfoSpan[4].innerHTML = "R" + MachineStatus.getFormatBit(currentStatistic.disk_read) + " | " + "W" + MachineStatus.getFormatBit(currentStatistic.disk_write); - hostInfoSpan[5].innerHTML = "⬇ " + MachineStatus.getFormatBit(currentStatistic.net_recv) + " | " + "⬆ " + MachineStatus.getFormatBit(currentStatistic.net_sent); + hostInfoSpan[4].innerHTML = "R " + MachineStatus.getFormatBit(currentStatistic.disk_read) + " | " + "W " + MachineStatus.getFormatBit(currentStatistic.disk_write); + hostInfoSpan[5].innerHTML = "" + MachineStatus.getFormatBit(currentStatistic.net_recv) + " | " + "" + MachineStatus.getFormatBit(currentStatistic.net_sent); hostInfoSpan[6].innerHTML = MachineStatus.getFormatSeconds(currentStatistic.boot_seconds || 0, this.language.days, this.language.hours, this.language.minutes, this.language.seconds); @@ -410,7 +410,8 @@ axisLabel: { formatter: (value) => { if (tableName === 'networkIO') { - return MachineStatus.getFormatBit(value); + let result = MachineStatus.getFormatBit(value, 0); + return result.substring(0, result.length - 2); } return value + (titleText === 'Load Avg' ? '' : '%') }, @@ -438,7 +439,7 @@ }); } if (tableName === 'networkIO') { - baseData.legend.data = ['net_recv', 'net_sent']; + baseData.legend.data = ['recv', 'sent']; baseData.series = []; baseData.legend.data.forEach((name) => { baseData.series.push({ @@ -492,26 +493,26 @@ }; /* Get format Bit */ - static getFormatBit(value) { + static getFormatBit(value, fixed = 2) { let suffix; - let b_value = value; + let b_value = value / 8; if (b_value >= 10e11) { - b_value = (b_value / 10e11).toFixed(2); - suffix = "tb/s"; + b_value = (b_value / 10e11).toFixed(fixed); + suffix = "TB/s"; } else if (b_value >= 10e8) { - b_value = (b_value / 10e8).toFixed(2); - suffix = "gb/s"; + b_value = (b_value / 10e8).toFixed(fixed); + suffix = "GB/s"; } else if (b_value >= 10e5) { - b_value = (b_value / 10e5).toFixed(2); - suffix = "mb/s"; + b_value = (b_value / 10e5).toFixed(fixed); + suffix = "MB/s"; } else if (b_value >= 10e2) { - b_value = (b_value / 10e2).toFixed(2); - suffix = "kb/s"; + b_value = (b_value / 10e2).toFixed(fixed); + suffix = "KB/s"; } else { - suffix = "b/s"; + suffix = "B/s"; b_value = b_value.toFixed(2); } - return b_value + suffix; + return b_value + ' ' + suffix; }; /* Get format time */ diff --git a/packages/cjs/flask-state.min.js b/packages/cjs/flask-state.min.js index 97f1887..46b5612 100644 --- a/packages/cjs/flask-state.min.js +++ b/packages/cjs/flask-state.min.js @@ -10,4 +10,4 @@ 'use strict'; -module.exports=function(e){var t={};function s(a){if(t[a])return t[a].exports;var n=t[a]={i:a,l:!1,exports:{}};return e[a].call(n.exports,n,n.exports,s),n.l=!0,n.exports}return s.m=e,s.c=t,s.d=function(e,t,a){s.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:a})},s.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},s.t=function(e,t){if(1&t&&(e=s(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var a=Object.create(null);if(s.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)s.d(a,n,function(t){return e[t]}.bind(null,n));return a},s.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return s.d(t,"a",t),t},s.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},s.p="",s(s.s=0)}([function(e,t,s){"use strict";const a=85,n=75,i=10,o=5,r=1048576;class d{constructor(e){this.language=e,this.mobile=d.isMobile(),this.index=0,this.initFlaskStateContainer(this.mobile),this.setEventListener(),this.initFlaskStateLanguage(this.language),this.setInitParams(),this.mobile&&this.setTagChangeEventListener(this.consoleCpuChart,this.consoleMemoryChart,this.consoleLoadavgChart,this.consoleDiskUsageChart),window.addEventListener("resize",(()=>{d.resizeChartTimer([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])}))}setFlaskStateData(){document.getElementById("fs-background").style.display="block",document.getElementById("fs-info-container").style.display="block",document.getElementsByTagName("body")[0].style.overflowX="hidden",document.getElementsByTagName("body")[0].style.overflowY="hidden",document.getElementById("fs-select-days").value="1",this.setCharts("1")}initFlaskStateContainer(){let e='

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Disk IO
Network IO
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
'+(this.mobile?'
':"
")+"
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e)}setEventListener(){if(window.addEventListener){document.getElementById("fs-info-close").addEventListener("click",(function(){document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out")})),document.getElementById("fs-background").addEventListener("click",(function(e){"fs-background"===String(e.target.id)&&(document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out"))}));let e=document.getElementById("fs-select-days");e.addEventListener("change",(()=>{this.setCharts(e.value)}))}}setTagChangeEventListener(...e){if(document.getElementById("fs-info-tab")){let t=document.getElementById("fs-info-tab").getElementsByTagName("li"),s=document.getElementById("fs-info-tab-memory"),a=t[0],n=0;const i={0:"fs-info-tab-memory",1:"fs-info-tab-cpu",2:"fs-info-network-io",3:"fs-info-tab-loadavg"};for(let o of t){let t=document.getElementById(i[n]);o.children[0].addEventListener("click",(()=>{o.classList.add("active"),a.classList.remove("active"),s.classList.remove("fs-show"),t.classList.add("fs-show"),d.resizeChart(e),s=t,a=o})),n++}}}initFlaskStateLanguage(){0!==Object.keys(this.language).length&&(document.getElementById("fs-host-status-title").innerHTML=this.language.host_status,document.getElementById("fs-redis-status-title").innerHTML=this.language.redis_status,document.getElementById("fs-cpu").innerHTML=this.language.cpu,document.getElementById("fs-memory").innerHTML=this.language.memory,document.getElementById("fs-disk-usage").innerHTML=this.language.disk_usage,document.getElementById("fs-load-avg").innerHTML=this.language.load_avg,document.getElementById("fs-disk-io").innerHTML=this.language.disk_io,document.getElementById("fs-network-io").innerHTML=this.language.network_io,document.getElementById("fs-boot-seconds").innerHTML=this.language.boot_seconds,document.getElementById("fs-used-memory").innerHTML=this.language.used_memory,document.getElementById("fs-used-memory-rss").innerHTML=this.language.used_memory_rss,document.getElementById("fs-mem-fragmentation-ratio").innerHTML=this.language.mem_fragmentation_ratio,document.getElementById("fs-hits-ratio").innerHTML=this.language.hits_ratio,document.getElementById("fs-delta-hits-ratio").innerHTML=this.language.delta_hits_ratio,document.getElementById("fs-uptime-in-seconds").innerHTML=this.language.uptime_in_seconds,document.getElementById("fs-connected-clients").innerHTML=this.language.connected_clients,document.getElementById("fs-days").innerHTML=this.language.days)}setInitParams(){this.consoleCpuChart=echarts.init(document.getElementById("fs-info-cpu-chart"),null,{renderer:"svg"}),this.consoleMemoryChart=echarts.init(document.getElementById("fs-info-memory-chart"),null,{renderer:"svg"}),this.consoleLoadavgChart=echarts.init(document.getElementById("fs-info-loadavg-chart"),null,{renderer:"svg"}),this.consoleDiskUsageChart=echarts.init(document.getElementById("fs-info-networkio-chart"),null,{renderer:"svg"}),this.cpuOption=d.generateChatOption(this.mobile,this.language.cpu||"CPU","",this.language.today||"Today"),this.memoryOption=d.generateChatOption(this.mobile,this.language.memory||"Memory","",this.language.today||"Today"),this.networkIOOption=d.generateChatOption(this.mobile,this.language.network_io||"Network IO","networkIO",this.language.today||"Today"),this.loadavgOption=d.generateChatOption(this.mobile,"Load Avg","loadavg",this.language.minutes||"min")}setCharts(e){this.consoleCpuChart.showLoading(),this.consoleMemoryChart.showLoading(),this.consoleLoadavgChart.showLoading(),this.consoleDiskUsageChart.showLoading(),fetch("/v0/state/hoststatus",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json;charset=UTF-8"},body:JSON.stringify({timeQuantum:e})}).then((e=>{e.ok&&e.json().then((e=>{if(200!==e.code)return;const t=e.data;let s=t.currentStatistic;if(Object.keys(s).length){let e=document.getElementById("fs-host-status").getElementsByClassName("fs-badge-content");e[0].innerHTML=s.memory+"%",e[1].innerHTML=s.cpu+"%",e[2].innerHTML=s.disk_usage+"%",e[3].innerHTML=s.load_avg[0]+","+s.load_avg[1]+","+s.load_avg[2],e[4].innerHTML="R"+d.getFormatBit(s.disk_read)+" | W"+d.getFormatBit(s.disk_write),e[5].innerHTML="⬇ "+d.getFormatBit(s.net_recv)+" | ⬆ "+d.getFormatBit(s.net_sent),e[6].innerHTML=d.getFormatSeconds(s.boot_seconds||0,this.language.days,this.language.hours,this.language.minutes,this.language.seconds);["memory","cpu","disk_usage","load_avg"].forEach((function(t,r){let d;if("load_avg"===t){let e=(s.load_avg[0]+s.load_avg[1]+s.load_avg[2])/3;d=e>=o?e>=i?"background-red":"background-orange":"background-green"}else d=s[t]>=n?s.memory>=a?"background-red":"background-orange":"background-green";let l=e[r].classList;l.remove("background-green","background-orange","background-red"),l.add(d)}));let t=document.getElementById("fs-redis-status").getElementsByClassName("fs-badge-content"),l=["used_memory","used_memory_rss","mem_fragmentation_ratio","hits_ratio","delta_hits_ratio","uptime_in_seconds","connected_clients"],c=!0;for(let e of l)if(s[e]){c=!1;break}c?(document.getElementById("fs-redis-status-title").innerHTML="",document.getElementById("fs-redis-status-title").style.marginTop="0",document.getElementById("fs-redis-status").style.display="none"):l.forEach(((e,a)=>{switch(e){case"used_memory":case"used_memory_rss":t[a].innerHTML=Math.ceil(s[e]/r)+" M";break;case"mem_fragmentation_ratio":let n=s[e];if(t[a].innerHTML=s[e],null!=n&&n>1){let e=t[a].classList;e.remove("background-green"),e.add("background-red")}break;case"hits_ratio":case"delta_hits_ratio":t[a].innerHTML=s[e]+"%";break;case"uptime_in_seconds":t[a].innerHTML=d.getFormatSeconds(s[e],this.language.days,this.language.hours,this.language.minutes,this.language.seconds);break;case"connected_clients":t[a].innerHTML=s[e]}}))}const l=["ts","cpu","memory","load_avg"];t.items=t.items.map((e=>{let t={};return l.forEach(((s,a)=>{if("ts"===s)return t[s]=1e3*e[a];t[s]=e[a]})),t})),t.items.reverse(),t.io.reverse();let c=d.getChartsData(t.items),g=d.getIOChartsData(t.io),u=c.ts_list,m=c.cpu_list,f=c.memory_list,h=c.load_avg_list[0],p=c.load_avg_list[1],y=c.load_avg_list[2];this.networkIOOption.xAxis.data=g.ts_list,this.networkIOOption.series[0].data=g.net_recv,this.networkIOOption.series[1].data=g.net_sent,this.consoleDiskUsageChart.setOption(this.networkIOOption),this.memoryOption.xAxis.data=u,this.cpuOption.xAxis.data=u,this.loadavgOption.xAxis.data=u,this.memoryOption.series[0].data=f,this.cpuOption.series[0].data=m,this.loadavgOption.series[0].data=h,this.loadavgOption.series[1].data=p,this.loadavgOption.series[2].data=y,this.consoleMemoryChart.setOption(this.memoryOption),this.consoleCpuChart.setOption(this.cpuOption),this.consoleLoadavgChart.setOption(this.loadavgOption),d.resizeChart([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])})).then((()=>{this.consoleMemoryChart.hideLoading(),this.consoleCpuChart.hideLoading(),this.consoleLoadavgChart.hideLoading(),this.consoleDiskUsageChart.hideLoading()}))})).catch((function(e){}))}static isMobile(){let e=navigator.userAgent,t={trident:e.indexOf("Trident")>-1,presto:e.indexOf("Presto")>-1,webKit:e.indexOf("AppleWebKit")>-1,gecko:e.indexOf("Gecko")>-1&&-1===e.indexOf("KHTML"),mobile:e.match(/AppleWebKit.*Mobile.*/)&&!0,ios:e.match(/\(i[^;]+;( U;)? CPU.Mac OS X/)&&!0,android:e.indexOf("Android")>-1||e.indexOf("Linux")>-1,iPhone:e.indexOf("iPhone")>-1,iPad:e.indexOf("iPad")>-1,webApp:-1===e.indexOf("Safari"),wechat:e.indexOf("MicroMessenger")>-1,qq:e.match(/\sQQ/i)&&" qq"===e.match(/\sQQ/i)[0]};return t.iPhone||t.iPad||t.webApp||t.wechat||t.qq||t.ios||t.mobile||!1}static resizeChartTimer(e,t){clearTimeout(this.clearId),this.clearId=setTimeout((function(){d.resizeChart(e)}),t||200)}static resizeChart(e){e.forEach((e=>e.resize()))}static generateChatOption(e,t,s="",a=""){let n={color:"loadavg"===s?["#ffa726","#42a5f5","#66bb6a"]:"networkIO"===s?["#ffa726","#42a5f5"]:["#42a5f5"],title:{show:!e,text:t},tooltip:{trigger:"axis",formatter:e=>{let t=echarts.format.formatTime("yyyy-MM-dd hh:mm:ss",new Date(parseInt(e[0].axisValue)),!1)+"
";if("networkIO"===s){for(let s=0;s";return t}for(let a=0;a";return t}},legend:{data:[a],textStyle:{fontSize:14},show:"Load Avg"===t||"networkIO"===s},grid:{left:"3%",right:"4%",bottom:"3%",top:e?30:60,containLabel:!0},toolbox:{show:!e,feature:{saveAsImage:{title:" "}}},xAxis:{type:"category",boundaryGap:!1,axisLabel:{formatter:function(e){return echarts.format.formatTime("hh:mm",new Date(parseInt(e)),!1)}}},yAxis:{type:"value",axisLabel:{formatter:e=>"networkIO"===s?d.getFormatBit(e):e+("Load Avg"===t?"":"%")}},series:[{name:a,type:"line",symbol:"none",hoverAnimation:!1}]};return"loadavg"===s&&(n.legend.data=["1 "+a,"5 "+a,"15 "+a],n.series=[],n.legend.data.forEach((e=>{n.series.push({name:e,type:"line",symbol:"none",hoverAnimation:!1})}))),"networkIO"===s&&(n.legend.data=["net_recv","net_sent"],n.series=[],n.legend.data.forEach((e=>{n.series.push({name:e,type:"line",symbol:"none",hoverAnimation:!1})}))),n}static getChartsData(e){let t=[],s=[],a=[],n=[],i=[],o=[];for(let r=0;r=1e12?(s=(s/1e12).toFixed(2),t="tb/s"):s>=1e9?(s=(s/1e9).toFixed(2),t="gb/s"):s>=1e6?(s=(s/1e6).toFixed(2),t="mb/s"):s>=1e3?(s=(s/1e3).toFixed(2),t="kb/s"):(t="b/s",s=s.toFixed(2)),s+t}static getFormatSeconds(e,t="days",s="hours",a="min",n="seconds"){let i=parseInt(e),o=0,r=0,d=0,l="";return i>=60?(o=i/60,o>=60&&(r=o/60,o%=60),r>=24&&(d=r/24,r%=24)):l=i+n,o>0&&(l=Math.floor(o)+" "+a),r>0&&(l=Math.floor(r)+" "+s),d>0&&(l=Math.floor(d)+" "+t),l}}const l=function(){let e=null;return function(t){return e||(e=new d(t))}}();t.init=function(e){let t=null,s={};if(null!==e&&"object"==typeof e&&(t=e.hasOwnProperty("dom")?e.dom:null,s=e.hasOwnProperty("lang")&&e.lang.hasOwnProperty("language")?e.lang:{}),t instanceof HTMLElement){if(t.getAttribute("flaskState"))return;t.setAttribute("flaskState","true"),t.addEventListener("click",(()=>l(s).setFlaskStateData()))}else{if(document.getElementById("fs-state-circular"))return;let e="
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e);let t,n=document.getElementById("fs-state-circular");function a(e){n.style.top=e.clientY-t+300+"px"}n.onclick=function(){this.classList.add("fs-circular-out"),l(s).setFlaskStateData()},n.onmousedown=function(e){t=t||e.clientY,document.addEventListener("mousemove",a)},document.onmouseup=function(){document.removeEventListener("mousemove",a);const e=parseInt(n.style.top);n.classList.add("fs-circular-animation"),n.style.top=Math.min(Math.max(e,50),window.screen.height-200)+"px",setTimeout((()=>n.classList.remove("fs-circular-animation")),500)}}}}]); \ No newline at end of file +module.exports=function(e){var t={};function s(a){if(t[a])return t[a].exports;var n=t[a]={i:a,l:!1,exports:{}};return e[a].call(n.exports,n,n.exports,s),n.l=!0,n.exports}return s.m=e,s.c=t,s.d=function(e,t,a){s.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:a})},s.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},s.t=function(e,t){if(1&t&&(e=s(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var a=Object.create(null);if(s.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)s.d(a,n,function(t){return e[t]}.bind(null,n));return a},s.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return s.d(t,"a",t),t},s.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},s.p="",s(s.s=0)}([function(e,t,s){"use strict";const a=85,n=75,i=10,o=5,r=1048576;class d{constructor(e){this.language=e,this.mobile=d.isMobile(),this.index=0,this.initFlaskStateContainer(this.mobile),this.setEventListener(),this.initFlaskStateLanguage(this.language),this.setInitParams(),this.mobile&&this.setTagChangeEventListener(this.consoleCpuChart,this.consoleMemoryChart,this.consoleLoadavgChart,this.consoleDiskUsageChart),window.addEventListener("resize",(()=>{d.resizeChartTimer([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])}))}setFlaskStateData(){document.getElementById("fs-background").style.display="block",document.getElementById("fs-info-container").style.display="block",document.getElementsByTagName("body")[0].style.overflowX="hidden",document.getElementsByTagName("body")[0].style.overflowY="hidden",document.getElementById("fs-select-days").value="1",this.setCharts("1")}initFlaskStateContainer(){let e='

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Disk IO
Network IO
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
'+(this.mobile?'
':"
")+"
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e)}setEventListener(){if(window.addEventListener){document.getElementById("fs-info-close").addEventListener("click",(function(){document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out")})),document.getElementById("fs-background").addEventListener("click",(function(e){"fs-background"===String(e.target.id)&&(document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out"))}));let e=document.getElementById("fs-select-days");e.addEventListener("change",(()=>{this.setCharts(e.value)}))}}setTagChangeEventListener(...e){if(document.getElementById("fs-info-tab")){let t=document.getElementById("fs-info-tab").getElementsByTagName("li"),s=document.getElementById("fs-info-tab-memory"),a=t[0],n=0;const i={0:"fs-info-tab-memory",1:"fs-info-tab-cpu",2:"fs-info-network-io",3:"fs-info-tab-loadavg"};for(let o of t){let t=document.getElementById(i[n]);o.children[0].addEventListener("click",(()=>{o.classList.add("active"),a.classList.remove("active"),s.classList.remove("fs-show"),t.classList.add("fs-show"),d.resizeChart(e),s=t,a=o})),n++}}}initFlaskStateLanguage(){0!==Object.keys(this.language).length&&(document.getElementById("fs-host-status-title").innerHTML=this.language.host_status,document.getElementById("fs-redis-status-title").innerHTML=this.language.redis_status,document.getElementById("fs-cpu").innerHTML=this.language.cpu,document.getElementById("fs-memory").innerHTML=this.language.memory,document.getElementById("fs-disk-usage").innerHTML=this.language.disk_usage,document.getElementById("fs-load-avg").innerHTML=this.language.load_avg,document.getElementById("fs-disk-io").innerHTML=this.language.disk_io,document.getElementById("fs-network-io").innerHTML=this.language.network_io,document.getElementById("fs-boot-seconds").innerHTML=this.language.boot_seconds,document.getElementById("fs-used-memory").innerHTML=this.language.used_memory,document.getElementById("fs-used-memory-rss").innerHTML=this.language.used_memory_rss,document.getElementById("fs-mem-fragmentation-ratio").innerHTML=this.language.mem_fragmentation_ratio,document.getElementById("fs-hits-ratio").innerHTML=this.language.hits_ratio,document.getElementById("fs-delta-hits-ratio").innerHTML=this.language.delta_hits_ratio,document.getElementById("fs-uptime-in-seconds").innerHTML=this.language.uptime_in_seconds,document.getElementById("fs-connected-clients").innerHTML=this.language.connected_clients,document.getElementById("fs-days").innerHTML=this.language.days)}setInitParams(){this.consoleCpuChart=echarts.init(document.getElementById("fs-info-cpu-chart"),null,{renderer:"svg"}),this.consoleMemoryChart=echarts.init(document.getElementById("fs-info-memory-chart"),null,{renderer:"svg"}),this.consoleLoadavgChart=echarts.init(document.getElementById("fs-info-loadavg-chart"),null,{renderer:"svg"}),this.consoleDiskUsageChart=echarts.init(document.getElementById("fs-info-networkio-chart"),null,{renderer:"svg"}),this.cpuOption=d.generateChatOption(this.mobile,this.language.cpu||"CPU","",this.language.today||"Today"),this.memoryOption=d.generateChatOption(this.mobile,this.language.memory||"Memory","",this.language.today||"Today"),this.networkIOOption=d.generateChatOption(this.mobile,this.language.network_io||"Network IO","networkIO",this.language.today||"Today"),this.loadavgOption=d.generateChatOption(this.mobile,"Load Avg","loadavg",this.language.minutes||"min")}setCharts(e){this.consoleCpuChart.showLoading(),this.consoleMemoryChart.showLoading(),this.consoleLoadavgChart.showLoading(),this.consoleDiskUsageChart.showLoading(),fetch("/v0/state/hoststatus",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json;charset=UTF-8"},body:JSON.stringify({timeQuantum:e})}).then((e=>{e.ok&&e.json().then((e=>{if(200!==e.code)return;const t=e.data;let s=t.currentStatistic;if(Object.keys(s).length){let e=document.getElementById("fs-host-status").getElementsByClassName("fs-badge-content");e[0].innerHTML=s.memory+"%",e[1].innerHTML=s.cpu+"%",e[2].innerHTML=s.disk_usage+"%",e[3].innerHTML=s.load_avg[0]+","+s.load_avg[1]+","+s.load_avg[2],e[4].innerHTML="R "+d.getFormatBit(s.disk_read)+" | W "+d.getFormatBit(s.disk_write),e[5].innerHTML=""+d.getFormatBit(s.net_recv)+" | "+d.getFormatBit(s.net_sent),e[6].innerHTML=d.getFormatSeconds(s.boot_seconds||0,this.language.days,this.language.hours,this.language.minutes,this.language.seconds);["memory","cpu","disk_usage","load_avg"].forEach((function(t,r){let d;if("load_avg"===t){let e=(s.load_avg[0]+s.load_avg[1]+s.load_avg[2])/3;d=e>=o?e>=i?"background-red":"background-orange":"background-green"}else d=s[t]>=n?s.memory>=a?"background-red":"background-orange":"background-green";let l=e[r].classList;l.remove("background-green","background-orange","background-red"),l.add(d)}));let t=document.getElementById("fs-redis-status").getElementsByClassName("fs-badge-content"),l=["used_memory","used_memory_rss","mem_fragmentation_ratio","hits_ratio","delta_hits_ratio","uptime_in_seconds","connected_clients"],c=!0;for(let e of l)if(s[e]){c=!1;break}c?(document.getElementById("fs-redis-status-title").innerHTML="",document.getElementById("fs-redis-status-title").style.marginTop="0",document.getElementById("fs-redis-status").style.display="none"):l.forEach(((e,a)=>{switch(e){case"used_memory":case"used_memory_rss":t[a].innerHTML=Math.ceil(s[e]/r)+" M";break;case"mem_fragmentation_ratio":let n=s[e];if(t[a].innerHTML=s[e],null!=n&&n>1){let e=t[a].classList;e.remove("background-green"),e.add("background-red")}break;case"hits_ratio":case"delta_hits_ratio":t[a].innerHTML=s[e]+"%";break;case"uptime_in_seconds":t[a].innerHTML=d.getFormatSeconds(s[e],this.language.days,this.language.hours,this.language.minutes,this.language.seconds);break;case"connected_clients":t[a].innerHTML=s[e]}}))}const l=["ts","cpu","memory","load_avg"];t.items=t.items.map((e=>{let t={};return l.forEach(((s,a)=>{if("ts"===s)return t[s]=1e3*e[a];t[s]=e[a]})),t})),t.items.reverse(),t.io.reverse();let c=d.getChartsData(t.items),g=d.getIOChartsData(t.io),u=c.ts_list,m=c.cpu_list,f=c.memory_list,h=c.load_avg_list[0],p=c.load_avg_list[1],y=c.load_avg_list[2];this.networkIOOption.xAxis.data=g.ts_list,this.networkIOOption.series[0].data=g.net_recv,this.networkIOOption.series[1].data=g.net_sent,this.consoleDiskUsageChart.setOption(this.networkIOOption),this.memoryOption.xAxis.data=u,this.cpuOption.xAxis.data=u,this.loadavgOption.xAxis.data=u,this.memoryOption.series[0].data=f,this.cpuOption.series[0].data=m,this.loadavgOption.series[0].data=h,this.loadavgOption.series[1].data=p,this.loadavgOption.series[2].data=y,this.consoleMemoryChart.setOption(this.memoryOption),this.consoleCpuChart.setOption(this.cpuOption),this.consoleLoadavgChart.setOption(this.loadavgOption),d.resizeChart([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])})).then((()=>{this.consoleMemoryChart.hideLoading(),this.consoleCpuChart.hideLoading(),this.consoleLoadavgChart.hideLoading(),this.consoleDiskUsageChart.hideLoading()}))})).catch((function(e){}))}static isMobile(){let e=navigator.userAgent,t={trident:e.indexOf("Trident")>-1,presto:e.indexOf("Presto")>-1,webKit:e.indexOf("AppleWebKit")>-1,gecko:e.indexOf("Gecko")>-1&&-1===e.indexOf("KHTML"),mobile:e.match(/AppleWebKit.*Mobile.*/)&&!0,ios:e.match(/\(i[^;]+;( U;)? CPU.Mac OS X/)&&!0,android:e.indexOf("Android")>-1||e.indexOf("Linux")>-1,iPhone:e.indexOf("iPhone")>-1,iPad:e.indexOf("iPad")>-1,webApp:-1===e.indexOf("Safari"),wechat:e.indexOf("MicroMessenger")>-1,qq:e.match(/\sQQ/i)&&" qq"===e.match(/\sQQ/i)[0]};return t.iPhone||t.iPad||t.webApp||t.wechat||t.qq||t.ios||t.mobile||!1}static resizeChartTimer(e,t){clearTimeout(this.clearId),this.clearId=setTimeout((function(){d.resizeChart(e)}),t||200)}static resizeChart(e){e.forEach((e=>e.resize()))}static generateChatOption(e,t,s="",a=""){let n={color:"loadavg"===s?["#ffa726","#42a5f5","#66bb6a"]:"networkIO"===s?["#ffa726","#42a5f5"]:["#42a5f5"],title:{show:!e,text:t},tooltip:{trigger:"axis",formatter:e=>{let t=echarts.format.formatTime("yyyy-MM-dd hh:mm:ss",new Date(parseInt(e[0].axisValue)),!1)+"
";if("networkIO"===s){for(let s=0;s";return t}for(let a=0;a";return t}},legend:{data:[a],textStyle:{fontSize:14},show:"Load Avg"===t||"networkIO"===s},grid:{left:"3%",right:"4%",bottom:"3%",top:e?30:60,containLabel:!0},toolbox:{show:!e,feature:{saveAsImage:{title:" "}}},xAxis:{type:"category",boundaryGap:!1,axisLabel:{formatter:function(e){return echarts.format.formatTime("hh:mm",new Date(parseInt(e)),!1)}}},yAxis:{type:"value",axisLabel:{formatter:e=>{if("networkIO"===s){let t=d.getFormatBit(e,0);return t.substring(0,t.length-2)}return e+("Load Avg"===t?"":"%")}}},series:[{name:a,type:"line",symbol:"none",hoverAnimation:!1}]};return"loadavg"===s&&(n.legend.data=["1 "+a,"5 "+a,"15 "+a],n.series=[],n.legend.data.forEach((e=>{n.series.push({name:e,type:"line",symbol:"none",hoverAnimation:!1})}))),"networkIO"===s&&(n.legend.data=["recv","sent"],n.series=[],n.legend.data.forEach((e=>{n.series.push({name:e,type:"line",symbol:"none",hoverAnimation:!1})}))),n}static getChartsData(e){let t=[],s=[],a=[],n=[],i=[],o=[];for(let r=0;r=1e12?(a=(a/1e12).toFixed(t),s="TB/s"):a>=1e9?(a=(a/1e9).toFixed(t),s="GB/s"):a>=1e6?(a=(a/1e6).toFixed(t),s="MB/s"):a>=1e3?(a=(a/1e3).toFixed(t),s="KB/s"):(s="B/s",a=a.toFixed(2)),a+" "+s}static getFormatSeconds(e,t="days",s="hours",a="min",n="seconds"){let i=parseInt(e),o=0,r=0,d=0,l="";return i>=60?(o=i/60,o>=60&&(r=o/60,o%=60),r>=24&&(d=r/24,r%=24)):l=i+n,o>0&&(l=Math.floor(o)+" "+a),r>0&&(l=Math.floor(r)+" "+s),d>0&&(l=Math.floor(d)+" "+t),l}}const l=function(){let e=null;return function(t){return e||(e=new d(t))}}();t.init=function(e){let t=null,s={};if(null!==e&&"object"==typeof e&&(t=e.hasOwnProperty("dom")?e.dom:null,s=e.hasOwnProperty("lang")&&e.lang.hasOwnProperty("language")?e.lang:{}),t instanceof HTMLElement){if(t.getAttribute("flaskState"))return;t.setAttribute("flaskState","true"),t.addEventListener("click",(()=>l(s).setFlaskStateData()))}else{if(document.getElementById("fs-state-circular"))return;let e="
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e);let t,n=document.getElementById("fs-state-circular");function a(e){n.style.top=e.clientY-t+300+"px"}n.onclick=function(){this.classList.add("fs-circular-out"),l(s).setFlaskStateData()},n.onmousedown=function(e){t=t||e.clientY,document.addEventListener("mousemove",a)},document.onmouseup=function(){document.removeEventListener("mousemove",a);const e=parseInt(n.style.top);n.classList.add("fs-circular-animation"),n.style.top=Math.min(Math.max(e,50),window.screen.height-200)+"px",setTimeout((()=>n.classList.remove("fs-circular-animation")),500)}}}}]); \ No newline at end of file diff --git a/packages/umd/flask-state.js b/packages/umd/flask-state.js index 637281c..e11d08c 100644 --- a/packages/umd/flask-state.js +++ b/packages/umd/flask-state.js @@ -198,8 +198,8 @@ hostInfoSpan[1].innerHTML = currentStatistic.cpu + '%'; hostInfoSpan[2].innerHTML = currentStatistic.disk_usage + '%'; hostInfoSpan[3].innerHTML = currentStatistic.load_avg[0] + "," + currentStatistic.load_avg[1] + "," + currentStatistic.load_avg[2]; - hostInfoSpan[4].innerHTML = "R" + MachineStatus.getFormatBit(currentStatistic.disk_read) + " | " + "W" + MachineStatus.getFormatBit(currentStatistic.disk_write); - hostInfoSpan[5].innerHTML = "⬇ " + MachineStatus.getFormatBit(currentStatistic.net_recv) + " | " + "⬆ " + MachineStatus.getFormatBit(currentStatistic.net_sent); + hostInfoSpan[4].innerHTML = "R " + MachineStatus.getFormatBit(currentStatistic.disk_read) + " | " + "W " + MachineStatus.getFormatBit(currentStatistic.disk_write); + hostInfoSpan[5].innerHTML = "" + MachineStatus.getFormatBit(currentStatistic.net_recv) + " | " + "" + MachineStatus.getFormatBit(currentStatistic.net_sent); hostInfoSpan[6].innerHTML = MachineStatus.getFormatSeconds(currentStatistic.boot_seconds || 0, this.language.days, this.language.hours, this.language.minutes, this.language.seconds); @@ -414,7 +414,8 @@ axisLabel: { formatter: (value) => { if (tableName === 'networkIO') { - return MachineStatus.getFormatBit(value); + let result = MachineStatus.getFormatBit(value, 0); + return result.substring(0, result.length - 2); } return value + (titleText === 'Load Avg' ? '' : '%') }, @@ -442,7 +443,7 @@ }); } if (tableName === 'networkIO') { - baseData.legend.data = ['net_recv', 'net_sent']; + baseData.legend.data = ['recv', 'sent']; baseData.series = []; baseData.legend.data.forEach((name) => { baseData.series.push({ @@ -496,26 +497,26 @@ }; /* Get format Bit */ - static getFormatBit(value) { + static getFormatBit(value, fixed = 2) { let suffix; - let b_value = value; + let b_value = value / 8; if (b_value >= 10e11) { - b_value = (b_value / 10e11).toFixed(2); - suffix = "tb/s"; + b_value = (b_value / 10e11).toFixed(fixed); + suffix = "TB/s"; } else if (b_value >= 10e8) { - b_value = (b_value / 10e8).toFixed(2); - suffix = "gb/s"; + b_value = (b_value / 10e8).toFixed(fixed); + suffix = "GB/s"; } else if (b_value >= 10e5) { - b_value = (b_value / 10e5).toFixed(2); - suffix = "mb/s"; + b_value = (b_value / 10e5).toFixed(fixed); + suffix = "MB/s"; } else if (b_value >= 10e2) { - b_value = (b_value / 10e2).toFixed(2); - suffix = "kb/s"; + b_value = (b_value / 10e2).toFixed(fixed); + suffix = "KB/s"; } else { - suffix = "b/s"; + suffix = "B/s"; b_value = b_value.toFixed(2); } - return b_value + suffix; + return b_value + ' ' + suffix; }; /* Get format time */ diff --git a/packages/umd/flask-state.min.js b/packages/umd/flask-state.min.js index f5c5734..8fc2d9a 100644 --- a/packages/umd/flask-state.min.js +++ b/packages/umd/flask-state.min.js @@ -10,4 +10,4 @@ 'use strict'; -!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.flaskState=t():e.flaskState=t()}(window,(function(){return function(e){var t={};function s(a){if(t[a])return t[a].exports;var n=t[a]={i:a,l:!1,exports:{}};return e[a].call(n.exports,n,n.exports,s),n.l=!0,n.exports}return s.m=e,s.c=t,s.d=function(e,t,a){s.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:a})},s.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},s.t=function(e,t){if(1&t&&(e=s(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var a=Object.create(null);if(s.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)s.d(a,n,function(t){return e[t]}.bind(null,n));return a},s.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return s.d(t,"a",t),t},s.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},s.p="",s(s.s=0)}([function(e,t,s){"use strict";const a=85,n=75,i=10,o=5,r=1048576;class d{constructor(e){this.language=e,this.mobile=d.isMobile(),this.index=0,this.initFlaskStateContainer(this.mobile),this.setEventListener(),this.initFlaskStateLanguage(this.language),this.setInitParams(),this.mobile&&this.setTagChangeEventListener(this.consoleCpuChart,this.consoleMemoryChart,this.consoleLoadavgChart,this.consoleDiskUsageChart),window.addEventListener("resize",(()=>{d.resizeChartTimer([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])}))}setFlaskStateData(){document.getElementById("fs-background").style.display="block",document.getElementById("fs-info-container").style.display="block",document.getElementsByTagName("body")[0].style.overflowX="hidden",document.getElementsByTagName("body")[0].style.overflowY="hidden",document.getElementById("fs-select-days").value="1",this.setCharts("1")}initFlaskStateContainer(){let e='

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Disk IO
Network IO
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
'+(this.mobile?'
':"
")+"
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e)}setEventListener(){if(window.addEventListener){document.getElementById("fs-info-close").addEventListener("click",(function(){document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out")})),document.getElementById("fs-background").addEventListener("click",(function(e){"fs-background"===String(e.target.id)&&(document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out"))}));let e=document.getElementById("fs-select-days");e.addEventListener("change",(()=>{this.setCharts(e.value)}))}}setTagChangeEventListener(...e){if(document.getElementById("fs-info-tab")){let t=document.getElementById("fs-info-tab").getElementsByTagName("li"),s=document.getElementById("fs-info-tab-memory"),a=t[0],n=0;const i={0:"fs-info-tab-memory",1:"fs-info-tab-cpu",2:"fs-info-network-io",3:"fs-info-tab-loadavg"};for(let o of t){let t=document.getElementById(i[n]);o.children[0].addEventListener("click",(()=>{o.classList.add("active"),a.classList.remove("active"),s.classList.remove("fs-show"),t.classList.add("fs-show"),d.resizeChart(e),s=t,a=o})),n++}}}initFlaskStateLanguage(){0!==Object.keys(this.language).length&&(document.getElementById("fs-host-status-title").innerHTML=this.language.host_status,document.getElementById("fs-redis-status-title").innerHTML=this.language.redis_status,document.getElementById("fs-cpu").innerHTML=this.language.cpu,document.getElementById("fs-memory").innerHTML=this.language.memory,document.getElementById("fs-disk-usage").innerHTML=this.language.disk_usage,document.getElementById("fs-load-avg").innerHTML=this.language.load_avg,document.getElementById("fs-disk-io").innerHTML=this.language.disk_io,document.getElementById("fs-network-io").innerHTML=this.language.network_io,document.getElementById("fs-boot-seconds").innerHTML=this.language.boot_seconds,document.getElementById("fs-used-memory").innerHTML=this.language.used_memory,document.getElementById("fs-used-memory-rss").innerHTML=this.language.used_memory_rss,document.getElementById("fs-mem-fragmentation-ratio").innerHTML=this.language.mem_fragmentation_ratio,document.getElementById("fs-hits-ratio").innerHTML=this.language.hits_ratio,document.getElementById("fs-delta-hits-ratio").innerHTML=this.language.delta_hits_ratio,document.getElementById("fs-uptime-in-seconds").innerHTML=this.language.uptime_in_seconds,document.getElementById("fs-connected-clients").innerHTML=this.language.connected_clients,document.getElementById("fs-days").innerHTML=this.language.days)}setInitParams(){this.consoleCpuChart=echarts.init(document.getElementById("fs-info-cpu-chart"),null,{renderer:"svg"}),this.consoleMemoryChart=echarts.init(document.getElementById("fs-info-memory-chart"),null,{renderer:"svg"}),this.consoleLoadavgChart=echarts.init(document.getElementById("fs-info-loadavg-chart"),null,{renderer:"svg"}),this.consoleDiskUsageChart=echarts.init(document.getElementById("fs-info-networkio-chart"),null,{renderer:"svg"}),this.cpuOption=d.generateChatOption(this.mobile,this.language.cpu||"CPU","",this.language.today||"Today"),this.memoryOption=d.generateChatOption(this.mobile,this.language.memory||"Memory","",this.language.today||"Today"),this.networkIOOption=d.generateChatOption(this.mobile,this.language.network_io||"Network IO","networkIO",this.language.today||"Today"),this.loadavgOption=d.generateChatOption(this.mobile,"Load Avg","loadavg",this.language.minutes||"min")}setCharts(e){this.consoleCpuChart.showLoading(),this.consoleMemoryChart.showLoading(),this.consoleLoadavgChart.showLoading(),this.consoleDiskUsageChart.showLoading(),fetch("/v0/state/hoststatus",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json;charset=UTF-8"},body:JSON.stringify({timeQuantum:e})}).then((e=>{e.ok&&e.json().then((e=>{if(200!==e.code)return;const t=e.data;let s=t.currentStatistic;if(Object.keys(s).length){let e=document.getElementById("fs-host-status").getElementsByClassName("fs-badge-content");e[0].innerHTML=s.memory+"%",e[1].innerHTML=s.cpu+"%",e[2].innerHTML=s.disk_usage+"%",e[3].innerHTML=s.load_avg[0]+","+s.load_avg[1]+","+s.load_avg[2],e[4].innerHTML="R"+d.getFormatBit(s.disk_read)+" | W"+d.getFormatBit(s.disk_write),e[5].innerHTML="⬇ "+d.getFormatBit(s.net_recv)+" | ⬆ "+d.getFormatBit(s.net_sent),e[6].innerHTML=d.getFormatSeconds(s.boot_seconds||0,this.language.days,this.language.hours,this.language.minutes,this.language.seconds);["memory","cpu","disk_usage","load_avg"].forEach((function(t,r){let d;if("load_avg"===t){let e=(s.load_avg[0]+s.load_avg[1]+s.load_avg[2])/3;d=e>=o?e>=i?"background-red":"background-orange":"background-green"}else d=s[t]>=n?s.memory>=a?"background-red":"background-orange":"background-green";let l=e[r].classList;l.remove("background-green","background-orange","background-red"),l.add(d)}));let t=document.getElementById("fs-redis-status").getElementsByClassName("fs-badge-content"),l=["used_memory","used_memory_rss","mem_fragmentation_ratio","hits_ratio","delta_hits_ratio","uptime_in_seconds","connected_clients"],c=!0;for(let e of l)if(s[e]){c=!1;break}c?(document.getElementById("fs-redis-status-title").innerHTML="",document.getElementById("fs-redis-status-title").style.marginTop="0",document.getElementById("fs-redis-status").style.display="none"):l.forEach(((e,a)=>{switch(e){case"used_memory":case"used_memory_rss":t[a].innerHTML=Math.ceil(s[e]/r)+" M";break;case"mem_fragmentation_ratio":let n=s[e];if(t[a].innerHTML=s[e],null!=n&&n>1){let e=t[a].classList;e.remove("background-green"),e.add("background-red")}break;case"hits_ratio":case"delta_hits_ratio":t[a].innerHTML=s[e]+"%";break;case"uptime_in_seconds":t[a].innerHTML=d.getFormatSeconds(s[e],this.language.days,this.language.hours,this.language.minutes,this.language.seconds);break;case"connected_clients":t[a].innerHTML=s[e]}}))}const l=["ts","cpu","memory","load_avg"];t.items=t.items.map((e=>{let t={};return l.forEach(((s,a)=>{if("ts"===s)return t[s]=1e3*e[a];t[s]=e[a]})),t})),t.items.reverse(),t.io.reverse();let c=d.getChartsData(t.items),g=d.getIOChartsData(t.io),u=c.ts_list,m=c.cpu_list,f=c.memory_list,h=c.load_avg_list[0],p=c.load_avg_list[1],y=c.load_avg_list[2];this.networkIOOption.xAxis.data=g.ts_list,this.networkIOOption.series[0].data=g.net_recv,this.networkIOOption.series[1].data=g.net_sent,this.consoleDiskUsageChart.setOption(this.networkIOOption),this.memoryOption.xAxis.data=u,this.cpuOption.xAxis.data=u,this.loadavgOption.xAxis.data=u,this.memoryOption.series[0].data=f,this.cpuOption.series[0].data=m,this.loadavgOption.series[0].data=h,this.loadavgOption.series[1].data=p,this.loadavgOption.series[2].data=y,this.consoleMemoryChart.setOption(this.memoryOption),this.consoleCpuChart.setOption(this.cpuOption),this.consoleLoadavgChart.setOption(this.loadavgOption),d.resizeChart([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])})).then((()=>{this.consoleMemoryChart.hideLoading(),this.consoleCpuChart.hideLoading(),this.consoleLoadavgChart.hideLoading(),this.consoleDiskUsageChart.hideLoading()}))})).catch((function(e){}))}static isMobile(){let e=navigator.userAgent,t={trident:e.indexOf("Trident")>-1,presto:e.indexOf("Presto")>-1,webKit:e.indexOf("AppleWebKit")>-1,gecko:e.indexOf("Gecko")>-1&&-1===e.indexOf("KHTML"),mobile:e.match(/AppleWebKit.*Mobile.*/)&&!0,ios:e.match(/\(i[^;]+;( U;)? CPU.Mac OS X/)&&!0,android:e.indexOf("Android")>-1||e.indexOf("Linux")>-1,iPhone:e.indexOf("iPhone")>-1,iPad:e.indexOf("iPad")>-1,webApp:-1===e.indexOf("Safari"),wechat:e.indexOf("MicroMessenger")>-1,qq:e.match(/\sQQ/i)&&" qq"===e.match(/\sQQ/i)[0]};return t.iPhone||t.iPad||t.webApp||t.wechat||t.qq||t.ios||t.mobile||!1}static resizeChartTimer(e,t){clearTimeout(this.clearId),this.clearId=setTimeout((function(){d.resizeChart(e)}),t||200)}static resizeChart(e){e.forEach((e=>e.resize()))}static generateChatOption(e,t,s="",a=""){let n={color:"loadavg"===s?["#ffa726","#42a5f5","#66bb6a"]:"networkIO"===s?["#ffa726","#42a5f5"]:["#42a5f5"],title:{show:!e,text:t},tooltip:{trigger:"axis",formatter:e=>{let t=echarts.format.formatTime("yyyy-MM-dd hh:mm:ss",new Date(parseInt(e[0].axisValue)),!1)+"
";if("networkIO"===s){for(let s=0;s";return t}for(let a=0;a";return t}},legend:{data:[a],textStyle:{fontSize:14},show:"Load Avg"===t||"networkIO"===s},grid:{left:"3%",right:"4%",bottom:"3%",top:e?30:60,containLabel:!0},toolbox:{show:!e,feature:{saveAsImage:{title:" "}}},xAxis:{type:"category",boundaryGap:!1,axisLabel:{formatter:function(e){return echarts.format.formatTime("hh:mm",new Date(parseInt(e)),!1)}}},yAxis:{type:"value",axisLabel:{formatter:e=>"networkIO"===s?d.getFormatBit(e):e+("Load Avg"===t?"":"%")}},series:[{name:a,type:"line",symbol:"none",hoverAnimation:!1}]};return"loadavg"===s&&(n.legend.data=["1 "+a,"5 "+a,"15 "+a],n.series=[],n.legend.data.forEach((e=>{n.series.push({name:e,type:"line",symbol:"none",hoverAnimation:!1})}))),"networkIO"===s&&(n.legend.data=["net_recv","net_sent"],n.series=[],n.legend.data.forEach((e=>{n.series.push({name:e,type:"line",symbol:"none",hoverAnimation:!1})}))),n}static getChartsData(e){let t=[],s=[],a=[],n=[],i=[],o=[];for(let r=0;r=1e12?(s=(s/1e12).toFixed(2),t="tb/s"):s>=1e9?(s=(s/1e9).toFixed(2),t="gb/s"):s>=1e6?(s=(s/1e6).toFixed(2),t="mb/s"):s>=1e3?(s=(s/1e3).toFixed(2),t="kb/s"):(t="b/s",s=s.toFixed(2)),s+t}static getFormatSeconds(e,t="days",s="hours",a="min",n="seconds"){let i=parseInt(e),o=0,r=0,d=0,l="";return i>=60?(o=i/60,o>=60&&(r=o/60,o%=60),r>=24&&(d=r/24,r%=24)):l=i+n,o>0&&(l=Math.floor(o)+" "+a),r>0&&(l=Math.floor(r)+" "+s),d>0&&(l=Math.floor(d)+" "+t),l}}const l=function(){let e=null;return function(t){return e||(e=new d(t))}}();t.init=function(e){let t=null,s={};if(null!==e&&"object"==typeof e&&(t=e.hasOwnProperty("dom")?e.dom:null,s=e.hasOwnProperty("lang")&&e.lang.hasOwnProperty("language")?e.lang:{}),t instanceof HTMLElement){if(t.getAttribute("flaskState"))return;t.setAttribute("flaskState","true"),t.addEventListener("click",(()=>l(s).setFlaskStateData()))}else{if(document.getElementById("fs-state-circular"))return;let e="
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e);let t,n=document.getElementById("fs-state-circular");function a(e){n.style.top=e.clientY-t+300+"px"}n.onclick=function(){this.classList.add("fs-circular-out"),l(s).setFlaskStateData()},n.onmousedown=function(e){t=t||e.clientY,document.addEventListener("mousemove",a)},document.onmouseup=function(){document.removeEventListener("mousemove",a);const e=parseInt(n.style.top);n.classList.add("fs-circular-animation"),n.style.top=Math.min(Math.max(e,50),window.screen.height-200)+"px",setTimeout((()=>n.classList.remove("fs-circular-animation")),500)}}}}])})); \ No newline at end of file +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.flaskState=t():e.flaskState=t()}(window,(function(){return function(e){var t={};function s(a){if(t[a])return t[a].exports;var n=t[a]={i:a,l:!1,exports:{}};return e[a].call(n.exports,n,n.exports,s),n.l=!0,n.exports}return s.m=e,s.c=t,s.d=function(e,t,a){s.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:a})},s.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},s.t=function(e,t){if(1&t&&(e=s(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var a=Object.create(null);if(s.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)s.d(a,n,function(t){return e[t]}.bind(null,n));return a},s.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return s.d(t,"a",t),t},s.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},s.p="",s(s.s=0)}([function(e,t,s){"use strict";const a=85,n=75,i=10,o=5,r=1048576;class d{constructor(e){this.language=e,this.mobile=d.isMobile(),this.index=0,this.initFlaskStateContainer(this.mobile),this.setEventListener(),this.initFlaskStateLanguage(this.language),this.setInitParams(),this.mobile&&this.setTagChangeEventListener(this.consoleCpuChart,this.consoleMemoryChart,this.consoleLoadavgChart,this.consoleDiskUsageChart),window.addEventListener("resize",(()=>{d.resizeChartTimer([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])}))}setFlaskStateData(){document.getElementById("fs-background").style.display="block",document.getElementById("fs-info-container").style.display="block",document.getElementsByTagName("body")[0].style.overflowX="hidden",document.getElementsByTagName("body")[0].style.overflowY="hidden",document.getElementById("fs-select-days").value="1",this.setCharts("1")}initFlaskStateContainer(){let e='

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Disk IO
Network IO
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
'+(this.mobile?'
':"
")+"
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e)}setEventListener(){if(window.addEventListener){document.getElementById("fs-info-close").addEventListener("click",(function(){document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out")})),document.getElementById("fs-background").addEventListener("click",(function(e){"fs-background"===String(e.target.id)&&(document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out"))}));let e=document.getElementById("fs-select-days");e.addEventListener("change",(()=>{this.setCharts(e.value)}))}}setTagChangeEventListener(...e){if(document.getElementById("fs-info-tab")){let t=document.getElementById("fs-info-tab").getElementsByTagName("li"),s=document.getElementById("fs-info-tab-memory"),a=t[0],n=0;const i={0:"fs-info-tab-memory",1:"fs-info-tab-cpu",2:"fs-info-network-io",3:"fs-info-tab-loadavg"};for(let o of t){let t=document.getElementById(i[n]);o.children[0].addEventListener("click",(()=>{o.classList.add("active"),a.classList.remove("active"),s.classList.remove("fs-show"),t.classList.add("fs-show"),d.resizeChart(e),s=t,a=o})),n++}}}initFlaskStateLanguage(){0!==Object.keys(this.language).length&&(document.getElementById("fs-host-status-title").innerHTML=this.language.host_status,document.getElementById("fs-redis-status-title").innerHTML=this.language.redis_status,document.getElementById("fs-cpu").innerHTML=this.language.cpu,document.getElementById("fs-memory").innerHTML=this.language.memory,document.getElementById("fs-disk-usage").innerHTML=this.language.disk_usage,document.getElementById("fs-load-avg").innerHTML=this.language.load_avg,document.getElementById("fs-disk-io").innerHTML=this.language.disk_io,document.getElementById("fs-network-io").innerHTML=this.language.network_io,document.getElementById("fs-boot-seconds").innerHTML=this.language.boot_seconds,document.getElementById("fs-used-memory").innerHTML=this.language.used_memory,document.getElementById("fs-used-memory-rss").innerHTML=this.language.used_memory_rss,document.getElementById("fs-mem-fragmentation-ratio").innerHTML=this.language.mem_fragmentation_ratio,document.getElementById("fs-hits-ratio").innerHTML=this.language.hits_ratio,document.getElementById("fs-delta-hits-ratio").innerHTML=this.language.delta_hits_ratio,document.getElementById("fs-uptime-in-seconds").innerHTML=this.language.uptime_in_seconds,document.getElementById("fs-connected-clients").innerHTML=this.language.connected_clients,document.getElementById("fs-days").innerHTML=this.language.days)}setInitParams(){this.consoleCpuChart=echarts.init(document.getElementById("fs-info-cpu-chart"),null,{renderer:"svg"}),this.consoleMemoryChart=echarts.init(document.getElementById("fs-info-memory-chart"),null,{renderer:"svg"}),this.consoleLoadavgChart=echarts.init(document.getElementById("fs-info-loadavg-chart"),null,{renderer:"svg"}),this.consoleDiskUsageChart=echarts.init(document.getElementById("fs-info-networkio-chart"),null,{renderer:"svg"}),this.cpuOption=d.generateChatOption(this.mobile,this.language.cpu||"CPU","",this.language.today||"Today"),this.memoryOption=d.generateChatOption(this.mobile,this.language.memory||"Memory","",this.language.today||"Today"),this.networkIOOption=d.generateChatOption(this.mobile,this.language.network_io||"Network IO","networkIO",this.language.today||"Today"),this.loadavgOption=d.generateChatOption(this.mobile,"Load Avg","loadavg",this.language.minutes||"min")}setCharts(e){this.consoleCpuChart.showLoading(),this.consoleMemoryChart.showLoading(),this.consoleLoadavgChart.showLoading(),this.consoleDiskUsageChart.showLoading(),fetch("/v0/state/hoststatus",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json;charset=UTF-8"},body:JSON.stringify({timeQuantum:e})}).then((e=>{e.ok&&e.json().then((e=>{if(200!==e.code)return;const t=e.data;let s=t.currentStatistic;if(Object.keys(s).length){let e=document.getElementById("fs-host-status").getElementsByClassName("fs-badge-content");e[0].innerHTML=s.memory+"%",e[1].innerHTML=s.cpu+"%",e[2].innerHTML=s.disk_usage+"%",e[3].innerHTML=s.load_avg[0]+","+s.load_avg[1]+","+s.load_avg[2],e[4].innerHTML="R "+d.getFormatBit(s.disk_read)+" | W "+d.getFormatBit(s.disk_write),e[5].innerHTML=""+d.getFormatBit(s.net_recv)+" | "+d.getFormatBit(s.net_sent),e[6].innerHTML=d.getFormatSeconds(s.boot_seconds||0,this.language.days,this.language.hours,this.language.minutes,this.language.seconds);["memory","cpu","disk_usage","load_avg"].forEach((function(t,r){let d;if("load_avg"===t){let e=(s.load_avg[0]+s.load_avg[1]+s.load_avg[2])/3;d=e>=o?e>=i?"background-red":"background-orange":"background-green"}else d=s[t]>=n?s.memory>=a?"background-red":"background-orange":"background-green";let l=e[r].classList;l.remove("background-green","background-orange","background-red"),l.add(d)}));let t=document.getElementById("fs-redis-status").getElementsByClassName("fs-badge-content"),l=["used_memory","used_memory_rss","mem_fragmentation_ratio","hits_ratio","delta_hits_ratio","uptime_in_seconds","connected_clients"],c=!0;for(let e of l)if(s[e]){c=!1;break}c?(document.getElementById("fs-redis-status-title").innerHTML="",document.getElementById("fs-redis-status-title").style.marginTop="0",document.getElementById("fs-redis-status").style.display="none"):l.forEach(((e,a)=>{switch(e){case"used_memory":case"used_memory_rss":t[a].innerHTML=Math.ceil(s[e]/r)+" M";break;case"mem_fragmentation_ratio":let n=s[e];if(t[a].innerHTML=s[e],null!=n&&n>1){let e=t[a].classList;e.remove("background-green"),e.add("background-red")}break;case"hits_ratio":case"delta_hits_ratio":t[a].innerHTML=s[e]+"%";break;case"uptime_in_seconds":t[a].innerHTML=d.getFormatSeconds(s[e],this.language.days,this.language.hours,this.language.minutes,this.language.seconds);break;case"connected_clients":t[a].innerHTML=s[e]}}))}const l=["ts","cpu","memory","load_avg"];t.items=t.items.map((e=>{let t={};return l.forEach(((s,a)=>{if("ts"===s)return t[s]=1e3*e[a];t[s]=e[a]})),t})),t.items.reverse(),t.io.reverse();let c=d.getChartsData(t.items),g=d.getIOChartsData(t.io),u=c.ts_list,m=c.cpu_list,f=c.memory_list,h=c.load_avg_list[0],p=c.load_avg_list[1],y=c.load_avg_list[2];this.networkIOOption.xAxis.data=g.ts_list,this.networkIOOption.series[0].data=g.net_recv,this.networkIOOption.series[1].data=g.net_sent,this.consoleDiskUsageChart.setOption(this.networkIOOption),this.memoryOption.xAxis.data=u,this.cpuOption.xAxis.data=u,this.loadavgOption.xAxis.data=u,this.memoryOption.series[0].data=f,this.cpuOption.series[0].data=m,this.loadavgOption.series[0].data=h,this.loadavgOption.series[1].data=p,this.loadavgOption.series[2].data=y,this.consoleMemoryChart.setOption(this.memoryOption),this.consoleCpuChart.setOption(this.cpuOption),this.consoleLoadavgChart.setOption(this.loadavgOption),d.resizeChart([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])})).then((()=>{this.consoleMemoryChart.hideLoading(),this.consoleCpuChart.hideLoading(),this.consoleLoadavgChart.hideLoading(),this.consoleDiskUsageChart.hideLoading()}))})).catch((function(e){}))}static isMobile(){let e=navigator.userAgent,t={trident:e.indexOf("Trident")>-1,presto:e.indexOf("Presto")>-1,webKit:e.indexOf("AppleWebKit")>-1,gecko:e.indexOf("Gecko")>-1&&-1===e.indexOf("KHTML"),mobile:e.match(/AppleWebKit.*Mobile.*/)&&!0,ios:e.match(/\(i[^;]+;( U;)? CPU.Mac OS X/)&&!0,android:e.indexOf("Android")>-1||e.indexOf("Linux")>-1,iPhone:e.indexOf("iPhone")>-1,iPad:e.indexOf("iPad")>-1,webApp:-1===e.indexOf("Safari"),wechat:e.indexOf("MicroMessenger")>-1,qq:e.match(/\sQQ/i)&&" qq"===e.match(/\sQQ/i)[0]};return t.iPhone||t.iPad||t.webApp||t.wechat||t.qq||t.ios||t.mobile||!1}static resizeChartTimer(e,t){clearTimeout(this.clearId),this.clearId=setTimeout((function(){d.resizeChart(e)}),t||200)}static resizeChart(e){e.forEach((e=>e.resize()))}static generateChatOption(e,t,s="",a=""){let n={color:"loadavg"===s?["#ffa726","#42a5f5","#66bb6a"]:"networkIO"===s?["#ffa726","#42a5f5"]:["#42a5f5"],title:{show:!e,text:t},tooltip:{trigger:"axis",formatter:e=>{let t=echarts.format.formatTime("yyyy-MM-dd hh:mm:ss",new Date(parseInt(e[0].axisValue)),!1)+"
";if("networkIO"===s){for(let s=0;s";return t}for(let a=0;a";return t}},legend:{data:[a],textStyle:{fontSize:14},show:"Load Avg"===t||"networkIO"===s},grid:{left:"3%",right:"4%",bottom:"3%",top:e?30:60,containLabel:!0},toolbox:{show:!e,feature:{saveAsImage:{title:" "}}},xAxis:{type:"category",boundaryGap:!1,axisLabel:{formatter:function(e){return echarts.format.formatTime("hh:mm",new Date(parseInt(e)),!1)}}},yAxis:{type:"value",axisLabel:{formatter:e=>{if("networkIO"===s){let t=d.getFormatBit(e,0);return t.substring(0,t.length-2)}return e+("Load Avg"===t?"":"%")}}},series:[{name:a,type:"line",symbol:"none",hoverAnimation:!1}]};return"loadavg"===s&&(n.legend.data=["1 "+a,"5 "+a,"15 "+a],n.series=[],n.legend.data.forEach((e=>{n.series.push({name:e,type:"line",symbol:"none",hoverAnimation:!1})}))),"networkIO"===s&&(n.legend.data=["recv","sent"],n.series=[],n.legend.data.forEach((e=>{n.series.push({name:e,type:"line",symbol:"none",hoverAnimation:!1})}))),n}static getChartsData(e){let t=[],s=[],a=[],n=[],i=[],o=[];for(let r=0;r=1e12?(a=(a/1e12).toFixed(t),s="TB/s"):a>=1e9?(a=(a/1e9).toFixed(t),s="GB/s"):a>=1e6?(a=(a/1e6).toFixed(t),s="MB/s"):a>=1e3?(a=(a/1e3).toFixed(t),s="KB/s"):(s="B/s",a=a.toFixed(2)),a+" "+s}static getFormatSeconds(e,t="days",s="hours",a="min",n="seconds"){let i=parseInt(e),o=0,r=0,d=0,l="";return i>=60?(o=i/60,o>=60&&(r=o/60,o%=60),r>=24&&(d=r/24,r%=24)):l=i+n,o>0&&(l=Math.floor(o)+" "+a),r>0&&(l=Math.floor(r)+" "+s),d>0&&(l=Math.floor(d)+" "+t),l}}const l=function(){let e=null;return function(t){return e||(e=new d(t))}}();t.init=function(e){let t=null,s={};if(null!==e&&"object"==typeof e&&(t=e.hasOwnProperty("dom")?e.dom:null,s=e.hasOwnProperty("lang")&&e.lang.hasOwnProperty("language")?e.lang:{}),t instanceof HTMLElement){if(t.getAttribute("flaskState"))return;t.setAttribute("flaskState","true"),t.addEventListener("click",(()=>l(s).setFlaskStateData()))}else{if(document.getElementById("fs-state-circular"))return;let e="
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e);let t,n=document.getElementById("fs-state-circular");function a(e){n.style.top=e.clientY-t+300+"px"}n.onclick=function(){this.classList.add("fs-circular-out"),l(s).setFlaskStateData()},n.onmousedown=function(e){t=t||e.clientY,document.addEventListener("mousemove",a)},document.onmouseup=function(){document.removeEventListener("mousemove",a);const e=parseInt(n.style.top);n.classList.add("fs-circular-animation"),n.style.top=Math.min(Math.max(e,50),window.screen.height-200)+"px",setTimeout((()=>n.classList.remove("fs-circular-animation")),500)}}}}])})); \ No newline at end of file From 17b7c92a43227863b0c58a4b7392de508c433dbe Mon Sep 17 00:00:00 2001 From: aaajiulong <525763728@qq.com> Date: Tue, 16 Feb 2021 18:40:10 +0800 Subject: [PATCH 15/20] fix: Fix psutil missing some attributes abnormal --- src/flask_state/services/host_status.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/flask_state/services/host_status.py b/src/flask_state/services/host_status.py index 0d01e05..10b1105 100644 --- a/src/flask_state/services/host_status.py +++ b/src/flask_state/services/host_status.py @@ -170,10 +170,16 @@ def query_host_io_info(): :return host status dict :rtype: dict """ - net_sent = psutil.net_io_counters().bytes_sent - net_recv = psutil.net_io_counters().bytes_recv - disk_read = psutil.disk_io_counters().read_bytes - disk_write = psutil.disk_io_counters().write_bytes + try: + net_sent = psutil.net_io_counters().bytes_sent + net_recv = psutil.net_io_counters().bytes_recv + disk_read = psutil.disk_io_counters().read_bytes + disk_write = psutil.disk_io_counters().write_bytes + except: + net_sent = 0 + net_recv = 0 + disk_read = 0 + disk_write = 0 result = { "net_sent": net_sent, "net_recv": net_recv, From 2f592ec034c6cf23c1e11c69cfd5d07764cffed3 Mon Sep 17 00:00:00 2001 From: aaajiulong <525763728@qq.com> Date: Tue, 16 Feb 2021 19:12:24 +0800 Subject: [PATCH 16/20] fix: Fix model data length is too short --- src/flask_state/models/flask_state_host.py | 2 +- src/flask_state/models/flask_state_io.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/flask_state/models/flask_state_host.py b/src/flask_state/models/flask_state_host.py index 8dfd722..a3d4b30 100644 --- a/src/flask_state/models/flask_state_host.py +++ b/src/flask_state/models/flask_state_host.py @@ -22,7 +22,7 @@ class FlaskStateHost(db.Model): load_avg = db.Column(String(32), server_default="") disk_usage = db.Column(FLOAT(unsigned=True), server_default=text("0")) boot_seconds = db.Column(INTEGER(unsigned=True), server_default=text("0")) - ts = db.Column(INTEGER(unsigned=True), server_default=text("0")) + ts = db.Column(BIGINT(unsigned=True), server_default=text("0")) # redis used_memory = db.Column(INTEGER(unsigned=True), server_default=text("0")) diff --git a/src/flask_state/models/flask_state_io.py b/src/flask_state/models/flask_state_io.py index 0750d76..fb79efa 100644 --- a/src/flask_state/models/flask_state_io.py +++ b/src/flask_state/models/flask_state_io.py @@ -23,7 +23,7 @@ class FlaskStateIO(db.Model): # disk disk_read = db.Column(BIGINT(unsigned=True), server_default=text("0")) disk_write = db.Column(BIGINT(unsigned=True), server_default=text("0")) - ts = db.Column(INTEGER(unsigned=True), server_default=text("0")) + ts = db.Column(BIGINT(unsigned=True), server_default=text("0")) __table_args__ = ( db.PrimaryKeyConstraint("id"), db.Index("idx_ct", create_time.desc()), From 916f81daf2274d73236376b31f98b1e9edd07563 Mon Sep 17 00:00:00 2001 From: aaajiulong <525763728@qq.com> Date: Wed, 17 Feb 2021 13:38:35 +0800 Subject: [PATCH 17/20] style: Modify display style --- examples/static/initial.js | 6 +++--- packages/cjs/flask-state.js | 6 +++--- packages/cjs/flask-state.min.js | 2 +- packages/flask-state.css | 4 ++++ packages/flask-state.min.css | 2 +- packages/umd/flask-state.js | 6 +++--- packages/umd/flask-state.min.js | 2 +- 7 files changed, 16 insertions(+), 12 deletions(-) diff --git a/examples/static/initial.js b/examples/static/initial.js index 3d1f652..3d87e42 100644 --- a/examples/static/initial.js +++ b/examples/static/initial.js @@ -45,7 +45,7 @@ class MachineStatus { initFlaskStateContainer() { let _chart = this.mobile ? `
` : `
`; - let _content = `

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Disk IO
Network IO
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
` + _chart + `
`; + let _content = `

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Disk IO
Network IO
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
` + _chart + `
`; document.getElementsByTagName('body')[0].insertAdjacentHTML('beforeend', _content); } @@ -181,8 +181,8 @@ class MachineStatus { hostInfoSpan[1].innerHTML = currentStatistic.cpu + '%'; hostInfoSpan[2].innerHTML = currentStatistic.disk_usage + '%'; hostInfoSpan[3].innerHTML = currentStatistic.load_avg[0] + "," + currentStatistic.load_avg[1] + "," + currentStatistic.load_avg[2]; - hostInfoSpan[4].innerHTML = "R " + MachineStatus.getFormatBit(currentStatistic.disk_read) + " | " + "W " + MachineStatus.getFormatBit(currentStatistic.disk_write); - hostInfoSpan[5].innerHTML = "" + MachineStatus.getFormatBit(currentStatistic.net_recv) + " | " + "" + MachineStatus.getFormatBit(currentStatistic.net_sent); + hostInfoSpan[4].innerHTML = "R " + MachineStatus.getFormatBit(currentStatistic.disk_read) + " | " + "W " + MachineStatus.getFormatBit(currentStatistic.disk_write) + ""; + hostInfoSpan[5].innerHTML = "⬇ " + MachineStatus.getFormatBit(currentStatistic.net_recv) + " | " + "⬆ " + MachineStatus.getFormatBit(currentStatistic.net_sent) + ""; hostInfoSpan[6].innerHTML = MachineStatus.getFormatSeconds(currentStatistic.boot_seconds || 0, this.language.days, this.language.hours, this.language.minutes, this.language.seconds); diff --git a/packages/cjs/flask-state.js b/packages/cjs/flask-state.js index b454722..b405ad9 100644 --- a/packages/cjs/flask-state.js +++ b/packages/cjs/flask-state.js @@ -58,7 +58,7 @@ initFlaskStateContainer() { let _chart = this.mobile ? `
` : `
`; - let _content = `

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Disk IO
Network IO
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
` + _chart + `
`; + let _content = `

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Disk IO
Network IO
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
` + _chart + `
`; document.getElementsByTagName('body')[0].insertAdjacentHTML('beforeend', _content); } @@ -194,8 +194,8 @@ hostInfoSpan[1].innerHTML = currentStatistic.cpu + '%'; hostInfoSpan[2].innerHTML = currentStatistic.disk_usage + '%'; hostInfoSpan[3].innerHTML = currentStatistic.load_avg[0] + "," + currentStatistic.load_avg[1] + "," + currentStatistic.load_avg[2]; - hostInfoSpan[4].innerHTML = "R " + MachineStatus.getFormatBit(currentStatistic.disk_read) + " | " + "W " + MachineStatus.getFormatBit(currentStatistic.disk_write); - hostInfoSpan[5].innerHTML = "" + MachineStatus.getFormatBit(currentStatistic.net_recv) + " | " + "" + MachineStatus.getFormatBit(currentStatistic.net_sent); + hostInfoSpan[4].innerHTML = "R " + MachineStatus.getFormatBit(currentStatistic.disk_read) + " | " + "W " + MachineStatus.getFormatBit(currentStatistic.disk_write) + ""; + hostInfoSpan[5].innerHTML = "⬇ " + MachineStatus.getFormatBit(currentStatistic.net_recv) + " | " + "⬆ " + MachineStatus.getFormatBit(currentStatistic.net_sent) + ""; hostInfoSpan[6].innerHTML = MachineStatus.getFormatSeconds(currentStatistic.boot_seconds || 0, this.language.days, this.language.hours, this.language.minutes, this.language.seconds); diff --git a/packages/cjs/flask-state.min.js b/packages/cjs/flask-state.min.js index 46b5612..1b02322 100644 --- a/packages/cjs/flask-state.min.js +++ b/packages/cjs/flask-state.min.js @@ -10,4 +10,4 @@ 'use strict'; -module.exports=function(e){var t={};function s(a){if(t[a])return t[a].exports;var n=t[a]={i:a,l:!1,exports:{}};return e[a].call(n.exports,n,n.exports,s),n.l=!0,n.exports}return s.m=e,s.c=t,s.d=function(e,t,a){s.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:a})},s.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},s.t=function(e,t){if(1&t&&(e=s(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var a=Object.create(null);if(s.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)s.d(a,n,function(t){return e[t]}.bind(null,n));return a},s.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return s.d(t,"a",t),t},s.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},s.p="",s(s.s=0)}([function(e,t,s){"use strict";const a=85,n=75,i=10,o=5,r=1048576;class d{constructor(e){this.language=e,this.mobile=d.isMobile(),this.index=0,this.initFlaskStateContainer(this.mobile),this.setEventListener(),this.initFlaskStateLanguage(this.language),this.setInitParams(),this.mobile&&this.setTagChangeEventListener(this.consoleCpuChart,this.consoleMemoryChart,this.consoleLoadavgChart,this.consoleDiskUsageChart),window.addEventListener("resize",(()=>{d.resizeChartTimer([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])}))}setFlaskStateData(){document.getElementById("fs-background").style.display="block",document.getElementById("fs-info-container").style.display="block",document.getElementsByTagName("body")[0].style.overflowX="hidden",document.getElementsByTagName("body")[0].style.overflowY="hidden",document.getElementById("fs-select-days").value="1",this.setCharts("1")}initFlaskStateContainer(){let e='

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Disk IO
Network IO
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
'+(this.mobile?'
':"
")+"
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e)}setEventListener(){if(window.addEventListener){document.getElementById("fs-info-close").addEventListener("click",(function(){document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out")})),document.getElementById("fs-background").addEventListener("click",(function(e){"fs-background"===String(e.target.id)&&(document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out"))}));let e=document.getElementById("fs-select-days");e.addEventListener("change",(()=>{this.setCharts(e.value)}))}}setTagChangeEventListener(...e){if(document.getElementById("fs-info-tab")){let t=document.getElementById("fs-info-tab").getElementsByTagName("li"),s=document.getElementById("fs-info-tab-memory"),a=t[0],n=0;const i={0:"fs-info-tab-memory",1:"fs-info-tab-cpu",2:"fs-info-network-io",3:"fs-info-tab-loadavg"};for(let o of t){let t=document.getElementById(i[n]);o.children[0].addEventListener("click",(()=>{o.classList.add("active"),a.classList.remove("active"),s.classList.remove("fs-show"),t.classList.add("fs-show"),d.resizeChart(e),s=t,a=o})),n++}}}initFlaskStateLanguage(){0!==Object.keys(this.language).length&&(document.getElementById("fs-host-status-title").innerHTML=this.language.host_status,document.getElementById("fs-redis-status-title").innerHTML=this.language.redis_status,document.getElementById("fs-cpu").innerHTML=this.language.cpu,document.getElementById("fs-memory").innerHTML=this.language.memory,document.getElementById("fs-disk-usage").innerHTML=this.language.disk_usage,document.getElementById("fs-load-avg").innerHTML=this.language.load_avg,document.getElementById("fs-disk-io").innerHTML=this.language.disk_io,document.getElementById("fs-network-io").innerHTML=this.language.network_io,document.getElementById("fs-boot-seconds").innerHTML=this.language.boot_seconds,document.getElementById("fs-used-memory").innerHTML=this.language.used_memory,document.getElementById("fs-used-memory-rss").innerHTML=this.language.used_memory_rss,document.getElementById("fs-mem-fragmentation-ratio").innerHTML=this.language.mem_fragmentation_ratio,document.getElementById("fs-hits-ratio").innerHTML=this.language.hits_ratio,document.getElementById("fs-delta-hits-ratio").innerHTML=this.language.delta_hits_ratio,document.getElementById("fs-uptime-in-seconds").innerHTML=this.language.uptime_in_seconds,document.getElementById("fs-connected-clients").innerHTML=this.language.connected_clients,document.getElementById("fs-days").innerHTML=this.language.days)}setInitParams(){this.consoleCpuChart=echarts.init(document.getElementById("fs-info-cpu-chart"),null,{renderer:"svg"}),this.consoleMemoryChart=echarts.init(document.getElementById("fs-info-memory-chart"),null,{renderer:"svg"}),this.consoleLoadavgChart=echarts.init(document.getElementById("fs-info-loadavg-chart"),null,{renderer:"svg"}),this.consoleDiskUsageChart=echarts.init(document.getElementById("fs-info-networkio-chart"),null,{renderer:"svg"}),this.cpuOption=d.generateChatOption(this.mobile,this.language.cpu||"CPU","",this.language.today||"Today"),this.memoryOption=d.generateChatOption(this.mobile,this.language.memory||"Memory","",this.language.today||"Today"),this.networkIOOption=d.generateChatOption(this.mobile,this.language.network_io||"Network IO","networkIO",this.language.today||"Today"),this.loadavgOption=d.generateChatOption(this.mobile,"Load Avg","loadavg",this.language.minutes||"min")}setCharts(e){this.consoleCpuChart.showLoading(),this.consoleMemoryChart.showLoading(),this.consoleLoadavgChart.showLoading(),this.consoleDiskUsageChart.showLoading(),fetch("/v0/state/hoststatus",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json;charset=UTF-8"},body:JSON.stringify({timeQuantum:e})}).then((e=>{e.ok&&e.json().then((e=>{if(200!==e.code)return;const t=e.data;let s=t.currentStatistic;if(Object.keys(s).length){let e=document.getElementById("fs-host-status").getElementsByClassName("fs-badge-content");e[0].innerHTML=s.memory+"%",e[1].innerHTML=s.cpu+"%",e[2].innerHTML=s.disk_usage+"%",e[3].innerHTML=s.load_avg[0]+","+s.load_avg[1]+","+s.load_avg[2],e[4].innerHTML="R "+d.getFormatBit(s.disk_read)+" | W "+d.getFormatBit(s.disk_write),e[5].innerHTML=""+d.getFormatBit(s.net_recv)+" | "+d.getFormatBit(s.net_sent),e[6].innerHTML=d.getFormatSeconds(s.boot_seconds||0,this.language.days,this.language.hours,this.language.minutes,this.language.seconds);["memory","cpu","disk_usage","load_avg"].forEach((function(t,r){let d;if("load_avg"===t){let e=(s.load_avg[0]+s.load_avg[1]+s.load_avg[2])/3;d=e>=o?e>=i?"background-red":"background-orange":"background-green"}else d=s[t]>=n?s.memory>=a?"background-red":"background-orange":"background-green";let l=e[r].classList;l.remove("background-green","background-orange","background-red"),l.add(d)}));let t=document.getElementById("fs-redis-status").getElementsByClassName("fs-badge-content"),l=["used_memory","used_memory_rss","mem_fragmentation_ratio","hits_ratio","delta_hits_ratio","uptime_in_seconds","connected_clients"],c=!0;for(let e of l)if(s[e]){c=!1;break}c?(document.getElementById("fs-redis-status-title").innerHTML="",document.getElementById("fs-redis-status-title").style.marginTop="0",document.getElementById("fs-redis-status").style.display="none"):l.forEach(((e,a)=>{switch(e){case"used_memory":case"used_memory_rss":t[a].innerHTML=Math.ceil(s[e]/r)+" M";break;case"mem_fragmentation_ratio":let n=s[e];if(t[a].innerHTML=s[e],null!=n&&n>1){let e=t[a].classList;e.remove("background-green"),e.add("background-red")}break;case"hits_ratio":case"delta_hits_ratio":t[a].innerHTML=s[e]+"%";break;case"uptime_in_seconds":t[a].innerHTML=d.getFormatSeconds(s[e],this.language.days,this.language.hours,this.language.minutes,this.language.seconds);break;case"connected_clients":t[a].innerHTML=s[e]}}))}const l=["ts","cpu","memory","load_avg"];t.items=t.items.map((e=>{let t={};return l.forEach(((s,a)=>{if("ts"===s)return t[s]=1e3*e[a];t[s]=e[a]})),t})),t.items.reverse(),t.io.reverse();let c=d.getChartsData(t.items),g=d.getIOChartsData(t.io),u=c.ts_list,m=c.cpu_list,f=c.memory_list,h=c.load_avg_list[0],p=c.load_avg_list[1],y=c.load_avg_list[2];this.networkIOOption.xAxis.data=g.ts_list,this.networkIOOption.series[0].data=g.net_recv,this.networkIOOption.series[1].data=g.net_sent,this.consoleDiskUsageChart.setOption(this.networkIOOption),this.memoryOption.xAxis.data=u,this.cpuOption.xAxis.data=u,this.loadavgOption.xAxis.data=u,this.memoryOption.series[0].data=f,this.cpuOption.series[0].data=m,this.loadavgOption.series[0].data=h,this.loadavgOption.series[1].data=p,this.loadavgOption.series[2].data=y,this.consoleMemoryChart.setOption(this.memoryOption),this.consoleCpuChart.setOption(this.cpuOption),this.consoleLoadavgChart.setOption(this.loadavgOption),d.resizeChart([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])})).then((()=>{this.consoleMemoryChart.hideLoading(),this.consoleCpuChart.hideLoading(),this.consoleLoadavgChart.hideLoading(),this.consoleDiskUsageChart.hideLoading()}))})).catch((function(e){}))}static isMobile(){let e=navigator.userAgent,t={trident:e.indexOf("Trident")>-1,presto:e.indexOf("Presto")>-1,webKit:e.indexOf("AppleWebKit")>-1,gecko:e.indexOf("Gecko")>-1&&-1===e.indexOf("KHTML"),mobile:e.match(/AppleWebKit.*Mobile.*/)&&!0,ios:e.match(/\(i[^;]+;( U;)? CPU.Mac OS X/)&&!0,android:e.indexOf("Android")>-1||e.indexOf("Linux")>-1,iPhone:e.indexOf("iPhone")>-1,iPad:e.indexOf("iPad")>-1,webApp:-1===e.indexOf("Safari"),wechat:e.indexOf("MicroMessenger")>-1,qq:e.match(/\sQQ/i)&&" qq"===e.match(/\sQQ/i)[0]};return t.iPhone||t.iPad||t.webApp||t.wechat||t.qq||t.ios||t.mobile||!1}static resizeChartTimer(e,t){clearTimeout(this.clearId),this.clearId=setTimeout((function(){d.resizeChart(e)}),t||200)}static resizeChart(e){e.forEach((e=>e.resize()))}static generateChatOption(e,t,s="",a=""){let n={color:"loadavg"===s?["#ffa726","#42a5f5","#66bb6a"]:"networkIO"===s?["#ffa726","#42a5f5"]:["#42a5f5"],title:{show:!e,text:t},tooltip:{trigger:"axis",formatter:e=>{let t=echarts.format.formatTime("yyyy-MM-dd hh:mm:ss",new Date(parseInt(e[0].axisValue)),!1)+"
";if("networkIO"===s){for(let s=0;s";return t}for(let a=0;a";return t}},legend:{data:[a],textStyle:{fontSize:14},show:"Load Avg"===t||"networkIO"===s},grid:{left:"3%",right:"4%",bottom:"3%",top:e?30:60,containLabel:!0},toolbox:{show:!e,feature:{saveAsImage:{title:" "}}},xAxis:{type:"category",boundaryGap:!1,axisLabel:{formatter:function(e){return echarts.format.formatTime("hh:mm",new Date(parseInt(e)),!1)}}},yAxis:{type:"value",axisLabel:{formatter:e=>{if("networkIO"===s){let t=d.getFormatBit(e,0);return t.substring(0,t.length-2)}return e+("Load Avg"===t?"":"%")}}},series:[{name:a,type:"line",symbol:"none",hoverAnimation:!1}]};return"loadavg"===s&&(n.legend.data=["1 "+a,"5 "+a,"15 "+a],n.series=[],n.legend.data.forEach((e=>{n.series.push({name:e,type:"line",symbol:"none",hoverAnimation:!1})}))),"networkIO"===s&&(n.legend.data=["recv","sent"],n.series=[],n.legend.data.forEach((e=>{n.series.push({name:e,type:"line",symbol:"none",hoverAnimation:!1})}))),n}static getChartsData(e){let t=[],s=[],a=[],n=[],i=[],o=[];for(let r=0;r=1e12?(a=(a/1e12).toFixed(t),s="TB/s"):a>=1e9?(a=(a/1e9).toFixed(t),s="GB/s"):a>=1e6?(a=(a/1e6).toFixed(t),s="MB/s"):a>=1e3?(a=(a/1e3).toFixed(t),s="KB/s"):(s="B/s",a=a.toFixed(2)),a+" "+s}static getFormatSeconds(e,t="days",s="hours",a="min",n="seconds"){let i=parseInt(e),o=0,r=0,d=0,l="";return i>=60?(o=i/60,o>=60&&(r=o/60,o%=60),r>=24&&(d=r/24,r%=24)):l=i+n,o>0&&(l=Math.floor(o)+" "+a),r>0&&(l=Math.floor(r)+" "+s),d>0&&(l=Math.floor(d)+" "+t),l}}const l=function(){let e=null;return function(t){return e||(e=new d(t))}}();t.init=function(e){let t=null,s={};if(null!==e&&"object"==typeof e&&(t=e.hasOwnProperty("dom")?e.dom:null,s=e.hasOwnProperty("lang")&&e.lang.hasOwnProperty("language")?e.lang:{}),t instanceof HTMLElement){if(t.getAttribute("flaskState"))return;t.setAttribute("flaskState","true"),t.addEventListener("click",(()=>l(s).setFlaskStateData()))}else{if(document.getElementById("fs-state-circular"))return;let e="
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e);let t,n=document.getElementById("fs-state-circular");function a(e){n.style.top=e.clientY-t+300+"px"}n.onclick=function(){this.classList.add("fs-circular-out"),l(s).setFlaskStateData()},n.onmousedown=function(e){t=t||e.clientY,document.addEventListener("mousemove",a)},document.onmouseup=function(){document.removeEventListener("mousemove",a);const e=parseInt(n.style.top);n.classList.add("fs-circular-animation"),n.style.top=Math.min(Math.max(e,50),window.screen.height-200)+"px",setTimeout((()=>n.classList.remove("fs-circular-animation")),500)}}}}]); \ No newline at end of file +module.exports=function(e){var t={};function s(a){if(t[a])return t[a].exports;var n=t[a]={i:a,l:!1,exports:{}};return e[a].call(n.exports,n,n.exports,s),n.l=!0,n.exports}return s.m=e,s.c=t,s.d=function(e,t,a){s.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:a})},s.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},s.t=function(e,t){if(1&t&&(e=s(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var a=Object.create(null);if(s.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)s.d(a,n,function(t){return e[t]}.bind(null,n));return a},s.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return s.d(t,"a",t),t},s.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},s.p="",s(s.s=0)}([function(e,t,s){"use strict";const a=85,n=75,i=10,o=5,r=1048576;class d{constructor(e){this.language=e,this.mobile=d.isMobile(),this.index=0,this.initFlaskStateContainer(this.mobile),this.setEventListener(),this.initFlaskStateLanguage(this.language),this.setInitParams(),this.mobile&&this.setTagChangeEventListener(this.consoleCpuChart,this.consoleMemoryChart,this.consoleLoadavgChart,this.consoleDiskUsageChart),window.addEventListener("resize",(()=>{d.resizeChartTimer([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])}))}setFlaskStateData(){document.getElementById("fs-background").style.display="block",document.getElementById("fs-info-container").style.display="block",document.getElementsByTagName("body")[0].style.overflowX="hidden",document.getElementsByTagName("body")[0].style.overflowY="hidden",document.getElementById("fs-select-days").value="1",this.setCharts("1")}initFlaskStateContainer(){let e='

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Disk IO
Network IO
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
'+(this.mobile?'
':"
")+"
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e)}setEventListener(){if(window.addEventListener){document.getElementById("fs-info-close").addEventListener("click",(function(){document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out")})),document.getElementById("fs-background").addEventListener("click",(function(e){"fs-background"===String(e.target.id)&&(document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out"))}));let e=document.getElementById("fs-select-days");e.addEventListener("change",(()=>{this.setCharts(e.value)}))}}setTagChangeEventListener(...e){if(document.getElementById("fs-info-tab")){let t=document.getElementById("fs-info-tab").getElementsByTagName("li"),s=document.getElementById("fs-info-tab-memory"),a=t[0],n=0;const i={0:"fs-info-tab-memory",1:"fs-info-tab-cpu",2:"fs-info-network-io",3:"fs-info-tab-loadavg"};for(let o of t){let t=document.getElementById(i[n]);o.children[0].addEventListener("click",(()=>{o.classList.add("active"),a.classList.remove("active"),s.classList.remove("fs-show"),t.classList.add("fs-show"),d.resizeChart(e),s=t,a=o})),n++}}}initFlaskStateLanguage(){0!==Object.keys(this.language).length&&(document.getElementById("fs-host-status-title").innerHTML=this.language.host_status,document.getElementById("fs-redis-status-title").innerHTML=this.language.redis_status,document.getElementById("fs-cpu").innerHTML=this.language.cpu,document.getElementById("fs-memory").innerHTML=this.language.memory,document.getElementById("fs-disk-usage").innerHTML=this.language.disk_usage,document.getElementById("fs-load-avg").innerHTML=this.language.load_avg,document.getElementById("fs-disk-io").innerHTML=this.language.disk_io,document.getElementById("fs-network-io").innerHTML=this.language.network_io,document.getElementById("fs-boot-seconds").innerHTML=this.language.boot_seconds,document.getElementById("fs-used-memory").innerHTML=this.language.used_memory,document.getElementById("fs-used-memory-rss").innerHTML=this.language.used_memory_rss,document.getElementById("fs-mem-fragmentation-ratio").innerHTML=this.language.mem_fragmentation_ratio,document.getElementById("fs-hits-ratio").innerHTML=this.language.hits_ratio,document.getElementById("fs-delta-hits-ratio").innerHTML=this.language.delta_hits_ratio,document.getElementById("fs-uptime-in-seconds").innerHTML=this.language.uptime_in_seconds,document.getElementById("fs-connected-clients").innerHTML=this.language.connected_clients,document.getElementById("fs-days").innerHTML=this.language.days)}setInitParams(){this.consoleCpuChart=echarts.init(document.getElementById("fs-info-cpu-chart"),null,{renderer:"svg"}),this.consoleMemoryChart=echarts.init(document.getElementById("fs-info-memory-chart"),null,{renderer:"svg"}),this.consoleLoadavgChart=echarts.init(document.getElementById("fs-info-loadavg-chart"),null,{renderer:"svg"}),this.consoleDiskUsageChart=echarts.init(document.getElementById("fs-info-networkio-chart"),null,{renderer:"svg"}),this.cpuOption=d.generateChatOption(this.mobile,this.language.cpu||"CPU","",this.language.today||"Today"),this.memoryOption=d.generateChatOption(this.mobile,this.language.memory||"Memory","",this.language.today||"Today"),this.networkIOOption=d.generateChatOption(this.mobile,this.language.network_io||"Network IO","networkIO",this.language.today||"Today"),this.loadavgOption=d.generateChatOption(this.mobile,"Load Avg","loadavg",this.language.minutes||"min")}setCharts(e){this.consoleCpuChart.showLoading(),this.consoleMemoryChart.showLoading(),this.consoleLoadavgChart.showLoading(),this.consoleDiskUsageChart.showLoading(),fetch("/v0/state/hoststatus",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json;charset=UTF-8"},body:JSON.stringify({timeQuantum:e})}).then((e=>{e.ok&&e.json().then((e=>{if(200!==e.code)return;const t=e.data;let s=t.currentStatistic;if(Object.keys(s).length){let e=document.getElementById("fs-host-status").getElementsByClassName("fs-badge-content");e[0].innerHTML=s.memory+"%",e[1].innerHTML=s.cpu+"%",e[2].innerHTML=s.disk_usage+"%",e[3].innerHTML=s.load_avg[0]+","+s.load_avg[1]+","+s.load_avg[2],e[4].innerHTML="R "+d.getFormatBit(s.disk_read)+" | W "+d.getFormatBit(s.disk_write)+"",e[5].innerHTML="⬇ "+d.getFormatBit(s.net_recv)+" | ⬆ "+d.getFormatBit(s.net_sent)+"",e[6].innerHTML=d.getFormatSeconds(s.boot_seconds||0,this.language.days,this.language.hours,this.language.minutes,this.language.seconds);["memory","cpu","disk_usage","load_avg"].forEach((function(t,r){let d;if("load_avg"===t){let e=(s.load_avg[0]+s.load_avg[1]+s.load_avg[2])/3;d=e>=o?e>=i?"background-red":"background-orange":"background-green"}else d=s[t]>=n?s.memory>=a?"background-red":"background-orange":"background-green";let l=e[r].classList;l.remove("background-green","background-orange","background-red"),l.add(d)}));let t=document.getElementById("fs-redis-status").getElementsByClassName("fs-badge-content"),l=["used_memory","used_memory_rss","mem_fragmentation_ratio","hits_ratio","delta_hits_ratio","uptime_in_seconds","connected_clients"],c=!0;for(let e of l)if(s[e]){c=!1;break}c?(document.getElementById("fs-redis-status-title").innerHTML="",document.getElementById("fs-redis-status-title").style.marginTop="0",document.getElementById("fs-redis-status").style.display="none"):l.forEach(((e,a)=>{switch(e){case"used_memory":case"used_memory_rss":t[a].innerHTML=Math.ceil(s[e]/r)+" M";break;case"mem_fragmentation_ratio":let n=s[e];if(t[a].innerHTML=s[e],null!=n&&n>1){let e=t[a].classList;e.remove("background-green"),e.add("background-red")}break;case"hits_ratio":case"delta_hits_ratio":t[a].innerHTML=s[e]+"%";break;case"uptime_in_seconds":t[a].innerHTML=d.getFormatSeconds(s[e],this.language.days,this.language.hours,this.language.minutes,this.language.seconds);break;case"connected_clients":t[a].innerHTML=s[e]}}))}const l=["ts","cpu","memory","load_avg"];t.items=t.items.map((e=>{let t={};return l.forEach(((s,a)=>{if("ts"===s)return t[s]=1e3*e[a];t[s]=e[a]})),t})),t.items.reverse(),t.io.reverse();let c=d.getChartsData(t.items),g=d.getIOChartsData(t.io),u=c.ts_list,m=c.cpu_list,f=c.memory_list,h=c.load_avg_list[0],p=c.load_avg_list[1],y=c.load_avg_list[2];this.networkIOOption.xAxis.data=g.ts_list,this.networkIOOption.series[0].data=g.net_recv,this.networkIOOption.series[1].data=g.net_sent,this.consoleDiskUsageChart.setOption(this.networkIOOption),this.memoryOption.xAxis.data=u,this.cpuOption.xAxis.data=u,this.loadavgOption.xAxis.data=u,this.memoryOption.series[0].data=f,this.cpuOption.series[0].data=m,this.loadavgOption.series[0].data=h,this.loadavgOption.series[1].data=p,this.loadavgOption.series[2].data=y,this.consoleMemoryChart.setOption(this.memoryOption),this.consoleCpuChart.setOption(this.cpuOption),this.consoleLoadavgChart.setOption(this.loadavgOption),d.resizeChart([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])})).then((()=>{this.consoleMemoryChart.hideLoading(),this.consoleCpuChart.hideLoading(),this.consoleLoadavgChart.hideLoading(),this.consoleDiskUsageChart.hideLoading()}))})).catch((function(e){}))}static isMobile(){let e=navigator.userAgent,t={trident:e.indexOf("Trident")>-1,presto:e.indexOf("Presto")>-1,webKit:e.indexOf("AppleWebKit")>-1,gecko:e.indexOf("Gecko")>-1&&-1===e.indexOf("KHTML"),mobile:e.match(/AppleWebKit.*Mobile.*/)&&!0,ios:e.match(/\(i[^;]+;( U;)? CPU.Mac OS X/)&&!0,android:e.indexOf("Android")>-1||e.indexOf("Linux")>-1,iPhone:e.indexOf("iPhone")>-1,iPad:e.indexOf("iPad")>-1,webApp:-1===e.indexOf("Safari"),wechat:e.indexOf("MicroMessenger")>-1,qq:e.match(/\sQQ/i)&&" qq"===e.match(/\sQQ/i)[0]};return t.iPhone||t.iPad||t.webApp||t.wechat||t.qq||t.ios||t.mobile||!1}static resizeChartTimer(e,t){clearTimeout(this.clearId),this.clearId=setTimeout((function(){d.resizeChart(e)}),t||200)}static resizeChart(e){e.forEach((e=>e.resize()))}static generateChatOption(e,t,s="",a=""){let n={color:"loadavg"===s?["#ffa726","#42a5f5","#66bb6a"]:"networkIO"===s?["#ffa726","#42a5f5"]:["#42a5f5"],title:{show:!e,text:t},tooltip:{trigger:"axis",formatter:e=>{let t=echarts.format.formatTime("yyyy-MM-dd hh:mm:ss",new Date(parseInt(e[0].axisValue)),!1)+"
";if("networkIO"===s){for(let s=0;s";return t}for(let a=0;a";return t}},legend:{data:[a],textStyle:{fontSize:14},show:"Load Avg"===t||"networkIO"===s},grid:{left:"3%",right:"4%",bottom:"3%",top:e?30:60,containLabel:!0},toolbox:{show:!e,feature:{saveAsImage:{title:" "}}},xAxis:{type:"category",boundaryGap:!1,axisLabel:{formatter:function(e){return echarts.format.formatTime("hh:mm",new Date(parseInt(e)),!1)}}},yAxis:{type:"value",axisLabel:{formatter:e=>{if("networkIO"===s){let t=d.getFormatBit(e,0);return t.substring(0,t.length-2)}return e+("Load Avg"===t?"":"%")}}},series:[{name:a,type:"line",symbol:"none",hoverAnimation:!1}]};return"loadavg"===s&&(n.legend.data=["1 "+a,"5 "+a,"15 "+a],n.series=[],n.legend.data.forEach((e=>{n.series.push({name:e,type:"line",symbol:"none",hoverAnimation:!1})}))),"networkIO"===s&&(n.legend.data=["recv","sent"],n.series=[],n.legend.data.forEach((e=>{n.series.push({name:e,type:"line",symbol:"none",hoverAnimation:!1})}))),n}static getChartsData(e){let t=[],s=[],a=[],n=[],i=[],o=[];for(let r=0;r=1e12?(a=(a/1e12).toFixed(t),s="TB/s"):a>=1e9?(a=(a/1e9).toFixed(t),s="GB/s"):a>=1e6?(a=(a/1e6).toFixed(t),s="MB/s"):a>=1e3?(a=(a/1e3).toFixed(t),s="KB/s"):(s="B/s",a=a.toFixed(2)),a+" "+s}static getFormatSeconds(e,t="days",s="hours",a="min",n="seconds"){let i=parseInt(e),o=0,r=0,d=0,l="";return i>=60?(o=i/60,o>=60&&(r=o/60,o%=60),r>=24&&(d=r/24,r%=24)):l=i+n,o>0&&(l=Math.floor(o)+" "+a),r>0&&(l=Math.floor(r)+" "+s),d>0&&(l=Math.floor(d)+" "+t),l}}const l=function(){let e=null;return function(t){return e||(e=new d(t))}}();t.init=function(e){let t=null,s={};if(null!==e&&"object"==typeof e&&(t=e.hasOwnProperty("dom")?e.dom:null,s=e.hasOwnProperty("lang")&&e.lang.hasOwnProperty("language")?e.lang:{}),t instanceof HTMLElement){if(t.getAttribute("flaskState"))return;t.setAttribute("flaskState","true"),t.addEventListener("click",(()=>l(s).setFlaskStateData()))}else{if(document.getElementById("fs-state-circular"))return;let e="
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e);let t,n=document.getElementById("fs-state-circular");function a(e){n.style.top=e.clientY-t+300+"px"}n.onclick=function(){this.classList.add("fs-circular-out"),l(s).setFlaskStateData()},n.onmousedown=function(e){t=t||e.clientY,document.addEventListener("mousemove",a)},document.onmouseup=function(){document.removeEventListener("mousemove",a);const e=parseInt(n.style.top);n.classList.add("fs-circular-animation"),n.style.top=Math.min(Math.max(e,50),window.screen.height-200)+"px",setTimeout((()=>n.classList.remove("fs-circular-animation")),500)}}}}]); \ No newline at end of file diff --git a/packages/flask-state.css b/packages/flask-state.css index 482662b..5780560 100644 --- a/packages/flask-state.css +++ b/packages/flask-state.css @@ -298,3 +298,7 @@ .b-99cb3d { background-color: #99cb3d !important; } + +.b-564970 { + background-color: #564970 !important; +} \ No newline at end of file diff --git a/packages/flask-state.min.css b/packages/flask-state.min.css index debdc87..827a644 100644 --- a/packages/flask-state.min.css +++ b/packages/flask-state.min.css @@ -1 +1 @@ -.flask-state-elem button,.flask-state-elem div,.flask-state-elem h4,.flask-state-elem li,.flask-state-elem span,.flask-state-elem svg,.flask-state-elem ul{font-size:14px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fs-background{position:fixed;width:100%;height:100%;overflow-y:auto;top:0;left:0;z-index:2001;background:hsla(0,0%,100%,.6)}.fs-background::-webkit-scrollbar-thumb{background-color:#bfbfbf;border-radius:10px}.fs-container-width{display:none;width:75%}.fs-container{position:relative;z-index:2002;display:block;margin:30px auto;padding:0 15px;background:#fff;border-top:3px solid #d2d6de;border-radius:3px;box-shadow:0 0 5px #ccc}.fs-select-container{z-index:3;position:absolute;top:10px;right:64px}.fs-select-arrow{position:absolute;display:flex;top:0;left:12px;padding-left:8px;align-items:center;pointer-events:none}.fs-select-days{margin-right:5px;padding:0 20px 0 5px;border-radius:3px;line-height:15px;appearance:none;-moz-appearance:none;-webkit-appearance:none}.fs-days,.fs-select-days{display:inline-block;vertical-align:top}.fs-days{margin:0;line-height:17px;text-transform:capitalize}.fs-close{z-index:2;position:absolute;top:6px;right:15px;padding:0;line-height:1;background:0 0;border:0;cursor:pointer}.fs-h4-style{margin-top:10px;margin-bottom:12px;font-size:18px!important;font-weight:500;line-height:15px}#fs-host-status>div,#fs-redis-status>div{display:inline-block;margin-bottom:10px;margin-right:18px}.fs-badge-intro{display:inline-block;padding:3px 5px;line-height:20px;color:#fff!important}.fs-badge-content{display:inline-block;height:26px;padding:0 8px;font-size:12px!important;font-weight:700;line-height:26px;color:#fff;text-align:center;white-space:nowrap;vertical-align:top}.console-info-line-style{margin:-6px -15px 6px;border-top:1px solid #ddd}.fs-ul-tabs{padding-left:0;margin:0;border-top-right-radius:3px;border-top-left-radius:3px;border-bottom:1px solid #f4f4f4;list-style:none}.fs-ul-tabs:after{clear:both;display:table;content:" ";-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fs-ul-tabs>li{float:left;margin:0 5px -2px 0;border-top:3px solid transparent}.fs-ul-tabs>li.active{border-top-color:#3c8dbc}.fs-ul-tabs>li>a{display:block;padding:10px 15px;line-height:1.42857143;text-decoration:none;color:#444;border-left:1px solid transparent;border-right:1px solid transparent}.fs-ul-tabs>li.active>a{border-color:#f4f4f4}.fs-mChart-box{display:none}.fs-mChart-box>div{max-height:250px;overflow-y:auto}.fs-show{display:block!important}.fs-chart-style{margin-top:10px;height:320px}.fs-chart-content{margin-top:10px;width:100%;max-height:710px;overflow-y:auto;overflow-x:hidden}.fs-chart-content::-webkit-scrollbar{padding-left:5px;width:5px}.fs-chart-content::-webkit-scrollbar-thumb{background-color:#bfbfbf;border-radius:10px}.fs-charts-width{float:left;width:50%}.fs-charts-box{position:relative;margin-bottom:20px;border-top:3px solid #00c0ef;border-radius:3px;box-shadow:0 1px 1px rgba(0,0,0,.1)}.fs-border{border-right:1px solid #d2d6de}.fs-circular{z-index:2000;position:fixed;right:15px;width:3rem;height:3rem;background-color:#fff;cursor:pointer}.fs-circular-animation{transition:.3s}.fs-circular-out{transform:translateX(100px);-ms-transform:translateX(100px);-webkit-transform:translateX(100px);transition:.3s}@media (max-width:768px){.fs-charts-width{float:left;width:100%}.fs-container-width{display:none;width:91.66666667%}}@media (max-width:992px){.fs-border{border:0}}.background-green{background-color:#00a65a!important}.background-red{background-color:#dd4b39!important}.background-orange{background-color:#ff851b!important}.b-0079cc{background-color:#0079cc!important}.b-007dc8{background-color:#007dc8!important}.b-0051b9{background-color:#0051b9!important}.b-534c6d{background-color:#534c6d!important}.b-99cb3d{background-color:#99cb3d!important} \ No newline at end of file +.flask-state-elem button,.flask-state-elem div,.flask-state-elem h4,.flask-state-elem li,.flask-state-elem span,.flask-state-elem svg,.flask-state-elem ul{font-size:14px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fs-background{position:fixed;width:100%;height:100%;overflow-y:auto;top:0;left:0;z-index:2001;background:hsla(0,0%,100%,.6)}.fs-background::-webkit-scrollbar-thumb{background-color:#bfbfbf;border-radius:10px}.fs-container-width{display:none;width:75%}.fs-container{position:relative;z-index:2002;display:block;margin:30px auto;padding:0 15px;background:#fff;border-top:3px solid #d2d6de;border-radius:3px;box-shadow:0 0 5px #ccc}.fs-select-container{z-index:3;position:absolute;top:10px;right:64px}.fs-select-arrow{position:absolute;display:flex;top:0;left:12px;padding-left:8px;align-items:center;pointer-events:none}.fs-select-days{margin-right:5px;padding:0 20px 0 5px;border-radius:3px;line-height:15px;appearance:none;-moz-appearance:none;-webkit-appearance:none}.fs-days,.fs-select-days{display:inline-block;vertical-align:top}.fs-days{margin:0;line-height:17px;text-transform:capitalize}.fs-close{z-index:2;position:absolute;top:6px;right:15px;padding:0;line-height:1;background:0 0;border:0;cursor:pointer}.fs-h4-style{margin-top:10px;margin-bottom:12px;font-size:18px!important;font-weight:500;line-height:15px}#fs-host-status>div,#fs-redis-status>div{display:inline-block;margin-bottom:10px;margin-right:18px}.fs-badge-intro{display:inline-block;padding:3px 5px;line-height:20px;color:#fff!important}.fs-badge-content{display:inline-block;height:26px;padding:0 8px;font-size:12px!important;font-weight:700;line-height:26px;color:#fff;text-align:center;white-space:nowrap;vertical-align:top}.console-info-line-style{margin:-6px -15px 6px;border-top:1px solid #ddd}.fs-ul-tabs{padding-left:0;margin:0;border-top-right-radius:3px;border-top-left-radius:3px;border-bottom:1px solid #f4f4f4;list-style:none}.fs-ul-tabs:after{clear:both;display:table;content:" ";-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fs-ul-tabs>li{float:left;margin:0 5px -2px 0;border-top:3px solid transparent}.fs-ul-tabs>li.active{border-top-color:#3c8dbc}.fs-ul-tabs>li>a{display:block;padding:10px 15px;line-height:1.42857143;text-decoration:none;color:#444;border-left:1px solid transparent;border-right:1px solid transparent}.fs-ul-tabs>li.active>a{border-color:#f4f4f4}.fs-mChart-box{display:none}.fs-mChart-box>div{max-height:250px;overflow-y:auto}.fs-show{display:block!important}.fs-chart-style{margin-top:10px;height:320px}.fs-chart-content{margin-top:10px;width:100%;max-height:710px;overflow-y:auto;overflow-x:hidden}.fs-chart-content::-webkit-scrollbar{padding-left:5px;width:5px}.fs-chart-content::-webkit-scrollbar-thumb{background-color:#bfbfbf;border-radius:10px}.fs-charts-width{float:left;width:50%}.fs-charts-box{position:relative;margin-bottom:20px;border-top:3px solid #00c0ef;border-radius:3px;box-shadow:0 1px 1px rgba(0,0,0,.1)}.fs-border{border-right:1px solid #d2d6de}.fs-circular{z-index:2000;position:fixed;right:15px;width:3rem;height:3rem;background-color:#fff;cursor:pointer}.fs-circular-animation{transition:.3s}.fs-circular-out{transform:translateX(100px);-ms-transform:translateX(100px);-webkit-transform:translateX(100px);transition:.3s}@media (max-width:768px){.fs-charts-width{float:left;width:100%}.fs-container-width{display:none;width:91.66666667%}}@media (max-width:992px){.fs-border{border:0}}.background-green{background-color:#00a65a!important}.background-red{background-color:#dd4b39!important}.background-orange{background-color:#ff851b!important}.b-0079cc{background-color:#0079cc!important}.b-007dc8{background-color:#007dc8!important}.b-0051b9{background-color:#0051b9!important}.b-534c6d{background-color:#534c6d!important}.b-99cb3d{background-color:#99cb3d!important}.b-564970{background-color:#564970!important} \ No newline at end of file diff --git a/packages/umd/flask-state.js b/packages/umd/flask-state.js index e11d08c..6c7f8e5 100644 --- a/packages/umd/flask-state.js +++ b/packages/umd/flask-state.js @@ -62,7 +62,7 @@ initFlaskStateContainer() { let _chart = this.mobile ? `
` : `
`; - let _content = `

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Disk IO
Network IO
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
` + _chart + `
`; + let _content = `

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Disk IO
Network IO
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
` + _chart + `
`; document.getElementsByTagName('body')[0].insertAdjacentHTML('beforeend', _content); } @@ -198,8 +198,8 @@ hostInfoSpan[1].innerHTML = currentStatistic.cpu + '%'; hostInfoSpan[2].innerHTML = currentStatistic.disk_usage + '%'; hostInfoSpan[3].innerHTML = currentStatistic.load_avg[0] + "," + currentStatistic.load_avg[1] + "," + currentStatistic.load_avg[2]; - hostInfoSpan[4].innerHTML = "R " + MachineStatus.getFormatBit(currentStatistic.disk_read) + " | " + "W " + MachineStatus.getFormatBit(currentStatistic.disk_write); - hostInfoSpan[5].innerHTML = "" + MachineStatus.getFormatBit(currentStatistic.net_recv) + " | " + "" + MachineStatus.getFormatBit(currentStatistic.net_sent); + hostInfoSpan[4].innerHTML = "R " + MachineStatus.getFormatBit(currentStatistic.disk_read) + " | " + "W " + MachineStatus.getFormatBit(currentStatistic.disk_write) + ""; + hostInfoSpan[5].innerHTML = "⬇ " + MachineStatus.getFormatBit(currentStatistic.net_recv) + " | " + "⬆ " + MachineStatus.getFormatBit(currentStatistic.net_sent) + ""; hostInfoSpan[6].innerHTML = MachineStatus.getFormatSeconds(currentStatistic.boot_seconds || 0, this.language.days, this.language.hours, this.language.minutes, this.language.seconds); diff --git a/packages/umd/flask-state.min.js b/packages/umd/flask-state.min.js index 8fc2d9a..edbe66e 100644 --- a/packages/umd/flask-state.min.js +++ b/packages/umd/flask-state.min.js @@ -10,4 +10,4 @@ 'use strict'; -!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.flaskState=t():e.flaskState=t()}(window,(function(){return function(e){var t={};function s(a){if(t[a])return t[a].exports;var n=t[a]={i:a,l:!1,exports:{}};return e[a].call(n.exports,n,n.exports,s),n.l=!0,n.exports}return s.m=e,s.c=t,s.d=function(e,t,a){s.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:a})},s.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},s.t=function(e,t){if(1&t&&(e=s(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var a=Object.create(null);if(s.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)s.d(a,n,function(t){return e[t]}.bind(null,n));return a},s.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return s.d(t,"a",t),t},s.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},s.p="",s(s.s=0)}([function(e,t,s){"use strict";const a=85,n=75,i=10,o=5,r=1048576;class d{constructor(e){this.language=e,this.mobile=d.isMobile(),this.index=0,this.initFlaskStateContainer(this.mobile),this.setEventListener(),this.initFlaskStateLanguage(this.language),this.setInitParams(),this.mobile&&this.setTagChangeEventListener(this.consoleCpuChart,this.consoleMemoryChart,this.consoleLoadavgChart,this.consoleDiskUsageChart),window.addEventListener("resize",(()=>{d.resizeChartTimer([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])}))}setFlaskStateData(){document.getElementById("fs-background").style.display="block",document.getElementById("fs-info-container").style.display="block",document.getElementsByTagName("body")[0].style.overflowX="hidden",document.getElementsByTagName("body")[0].style.overflowY="hidden",document.getElementById("fs-select-days").value="1",this.setCharts("1")}initFlaskStateContainer(){let e='

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Disk IO
Network IO
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
'+(this.mobile?'
':"
")+"
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e)}setEventListener(){if(window.addEventListener){document.getElementById("fs-info-close").addEventListener("click",(function(){document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out")})),document.getElementById("fs-background").addEventListener("click",(function(e){"fs-background"===String(e.target.id)&&(document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out"))}));let e=document.getElementById("fs-select-days");e.addEventListener("change",(()=>{this.setCharts(e.value)}))}}setTagChangeEventListener(...e){if(document.getElementById("fs-info-tab")){let t=document.getElementById("fs-info-tab").getElementsByTagName("li"),s=document.getElementById("fs-info-tab-memory"),a=t[0],n=0;const i={0:"fs-info-tab-memory",1:"fs-info-tab-cpu",2:"fs-info-network-io",3:"fs-info-tab-loadavg"};for(let o of t){let t=document.getElementById(i[n]);o.children[0].addEventListener("click",(()=>{o.classList.add("active"),a.classList.remove("active"),s.classList.remove("fs-show"),t.classList.add("fs-show"),d.resizeChart(e),s=t,a=o})),n++}}}initFlaskStateLanguage(){0!==Object.keys(this.language).length&&(document.getElementById("fs-host-status-title").innerHTML=this.language.host_status,document.getElementById("fs-redis-status-title").innerHTML=this.language.redis_status,document.getElementById("fs-cpu").innerHTML=this.language.cpu,document.getElementById("fs-memory").innerHTML=this.language.memory,document.getElementById("fs-disk-usage").innerHTML=this.language.disk_usage,document.getElementById("fs-load-avg").innerHTML=this.language.load_avg,document.getElementById("fs-disk-io").innerHTML=this.language.disk_io,document.getElementById("fs-network-io").innerHTML=this.language.network_io,document.getElementById("fs-boot-seconds").innerHTML=this.language.boot_seconds,document.getElementById("fs-used-memory").innerHTML=this.language.used_memory,document.getElementById("fs-used-memory-rss").innerHTML=this.language.used_memory_rss,document.getElementById("fs-mem-fragmentation-ratio").innerHTML=this.language.mem_fragmentation_ratio,document.getElementById("fs-hits-ratio").innerHTML=this.language.hits_ratio,document.getElementById("fs-delta-hits-ratio").innerHTML=this.language.delta_hits_ratio,document.getElementById("fs-uptime-in-seconds").innerHTML=this.language.uptime_in_seconds,document.getElementById("fs-connected-clients").innerHTML=this.language.connected_clients,document.getElementById("fs-days").innerHTML=this.language.days)}setInitParams(){this.consoleCpuChart=echarts.init(document.getElementById("fs-info-cpu-chart"),null,{renderer:"svg"}),this.consoleMemoryChart=echarts.init(document.getElementById("fs-info-memory-chart"),null,{renderer:"svg"}),this.consoleLoadavgChart=echarts.init(document.getElementById("fs-info-loadavg-chart"),null,{renderer:"svg"}),this.consoleDiskUsageChart=echarts.init(document.getElementById("fs-info-networkio-chart"),null,{renderer:"svg"}),this.cpuOption=d.generateChatOption(this.mobile,this.language.cpu||"CPU","",this.language.today||"Today"),this.memoryOption=d.generateChatOption(this.mobile,this.language.memory||"Memory","",this.language.today||"Today"),this.networkIOOption=d.generateChatOption(this.mobile,this.language.network_io||"Network IO","networkIO",this.language.today||"Today"),this.loadavgOption=d.generateChatOption(this.mobile,"Load Avg","loadavg",this.language.minutes||"min")}setCharts(e){this.consoleCpuChart.showLoading(),this.consoleMemoryChart.showLoading(),this.consoleLoadavgChart.showLoading(),this.consoleDiskUsageChart.showLoading(),fetch("/v0/state/hoststatus",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json;charset=UTF-8"},body:JSON.stringify({timeQuantum:e})}).then((e=>{e.ok&&e.json().then((e=>{if(200!==e.code)return;const t=e.data;let s=t.currentStatistic;if(Object.keys(s).length){let e=document.getElementById("fs-host-status").getElementsByClassName("fs-badge-content");e[0].innerHTML=s.memory+"%",e[1].innerHTML=s.cpu+"%",e[2].innerHTML=s.disk_usage+"%",e[3].innerHTML=s.load_avg[0]+","+s.load_avg[1]+","+s.load_avg[2],e[4].innerHTML="R "+d.getFormatBit(s.disk_read)+" | W "+d.getFormatBit(s.disk_write),e[5].innerHTML=""+d.getFormatBit(s.net_recv)+" | "+d.getFormatBit(s.net_sent),e[6].innerHTML=d.getFormatSeconds(s.boot_seconds||0,this.language.days,this.language.hours,this.language.minutes,this.language.seconds);["memory","cpu","disk_usage","load_avg"].forEach((function(t,r){let d;if("load_avg"===t){let e=(s.load_avg[0]+s.load_avg[1]+s.load_avg[2])/3;d=e>=o?e>=i?"background-red":"background-orange":"background-green"}else d=s[t]>=n?s.memory>=a?"background-red":"background-orange":"background-green";let l=e[r].classList;l.remove("background-green","background-orange","background-red"),l.add(d)}));let t=document.getElementById("fs-redis-status").getElementsByClassName("fs-badge-content"),l=["used_memory","used_memory_rss","mem_fragmentation_ratio","hits_ratio","delta_hits_ratio","uptime_in_seconds","connected_clients"],c=!0;for(let e of l)if(s[e]){c=!1;break}c?(document.getElementById("fs-redis-status-title").innerHTML="",document.getElementById("fs-redis-status-title").style.marginTop="0",document.getElementById("fs-redis-status").style.display="none"):l.forEach(((e,a)=>{switch(e){case"used_memory":case"used_memory_rss":t[a].innerHTML=Math.ceil(s[e]/r)+" M";break;case"mem_fragmentation_ratio":let n=s[e];if(t[a].innerHTML=s[e],null!=n&&n>1){let e=t[a].classList;e.remove("background-green"),e.add("background-red")}break;case"hits_ratio":case"delta_hits_ratio":t[a].innerHTML=s[e]+"%";break;case"uptime_in_seconds":t[a].innerHTML=d.getFormatSeconds(s[e],this.language.days,this.language.hours,this.language.minutes,this.language.seconds);break;case"connected_clients":t[a].innerHTML=s[e]}}))}const l=["ts","cpu","memory","load_avg"];t.items=t.items.map((e=>{let t={};return l.forEach(((s,a)=>{if("ts"===s)return t[s]=1e3*e[a];t[s]=e[a]})),t})),t.items.reverse(),t.io.reverse();let c=d.getChartsData(t.items),g=d.getIOChartsData(t.io),u=c.ts_list,m=c.cpu_list,f=c.memory_list,h=c.load_avg_list[0],p=c.load_avg_list[1],y=c.load_avg_list[2];this.networkIOOption.xAxis.data=g.ts_list,this.networkIOOption.series[0].data=g.net_recv,this.networkIOOption.series[1].data=g.net_sent,this.consoleDiskUsageChart.setOption(this.networkIOOption),this.memoryOption.xAxis.data=u,this.cpuOption.xAxis.data=u,this.loadavgOption.xAxis.data=u,this.memoryOption.series[0].data=f,this.cpuOption.series[0].data=m,this.loadavgOption.series[0].data=h,this.loadavgOption.series[1].data=p,this.loadavgOption.series[2].data=y,this.consoleMemoryChart.setOption(this.memoryOption),this.consoleCpuChart.setOption(this.cpuOption),this.consoleLoadavgChart.setOption(this.loadavgOption),d.resizeChart([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])})).then((()=>{this.consoleMemoryChart.hideLoading(),this.consoleCpuChart.hideLoading(),this.consoleLoadavgChart.hideLoading(),this.consoleDiskUsageChart.hideLoading()}))})).catch((function(e){}))}static isMobile(){let e=navigator.userAgent,t={trident:e.indexOf("Trident")>-1,presto:e.indexOf("Presto")>-1,webKit:e.indexOf("AppleWebKit")>-1,gecko:e.indexOf("Gecko")>-1&&-1===e.indexOf("KHTML"),mobile:e.match(/AppleWebKit.*Mobile.*/)&&!0,ios:e.match(/\(i[^;]+;( U;)? CPU.Mac OS X/)&&!0,android:e.indexOf("Android")>-1||e.indexOf("Linux")>-1,iPhone:e.indexOf("iPhone")>-1,iPad:e.indexOf("iPad")>-1,webApp:-1===e.indexOf("Safari"),wechat:e.indexOf("MicroMessenger")>-1,qq:e.match(/\sQQ/i)&&" qq"===e.match(/\sQQ/i)[0]};return t.iPhone||t.iPad||t.webApp||t.wechat||t.qq||t.ios||t.mobile||!1}static resizeChartTimer(e,t){clearTimeout(this.clearId),this.clearId=setTimeout((function(){d.resizeChart(e)}),t||200)}static resizeChart(e){e.forEach((e=>e.resize()))}static generateChatOption(e,t,s="",a=""){let n={color:"loadavg"===s?["#ffa726","#42a5f5","#66bb6a"]:"networkIO"===s?["#ffa726","#42a5f5"]:["#42a5f5"],title:{show:!e,text:t},tooltip:{trigger:"axis",formatter:e=>{let t=echarts.format.formatTime("yyyy-MM-dd hh:mm:ss",new Date(parseInt(e[0].axisValue)),!1)+"
";if("networkIO"===s){for(let s=0;s";return t}for(let a=0;a";return t}},legend:{data:[a],textStyle:{fontSize:14},show:"Load Avg"===t||"networkIO"===s},grid:{left:"3%",right:"4%",bottom:"3%",top:e?30:60,containLabel:!0},toolbox:{show:!e,feature:{saveAsImage:{title:" "}}},xAxis:{type:"category",boundaryGap:!1,axisLabel:{formatter:function(e){return echarts.format.formatTime("hh:mm",new Date(parseInt(e)),!1)}}},yAxis:{type:"value",axisLabel:{formatter:e=>{if("networkIO"===s){let t=d.getFormatBit(e,0);return t.substring(0,t.length-2)}return e+("Load Avg"===t?"":"%")}}},series:[{name:a,type:"line",symbol:"none",hoverAnimation:!1}]};return"loadavg"===s&&(n.legend.data=["1 "+a,"5 "+a,"15 "+a],n.series=[],n.legend.data.forEach((e=>{n.series.push({name:e,type:"line",symbol:"none",hoverAnimation:!1})}))),"networkIO"===s&&(n.legend.data=["recv","sent"],n.series=[],n.legend.data.forEach((e=>{n.series.push({name:e,type:"line",symbol:"none",hoverAnimation:!1})}))),n}static getChartsData(e){let t=[],s=[],a=[],n=[],i=[],o=[];for(let r=0;r=1e12?(a=(a/1e12).toFixed(t),s="TB/s"):a>=1e9?(a=(a/1e9).toFixed(t),s="GB/s"):a>=1e6?(a=(a/1e6).toFixed(t),s="MB/s"):a>=1e3?(a=(a/1e3).toFixed(t),s="KB/s"):(s="B/s",a=a.toFixed(2)),a+" "+s}static getFormatSeconds(e,t="days",s="hours",a="min",n="seconds"){let i=parseInt(e),o=0,r=0,d=0,l="";return i>=60?(o=i/60,o>=60&&(r=o/60,o%=60),r>=24&&(d=r/24,r%=24)):l=i+n,o>0&&(l=Math.floor(o)+" "+a),r>0&&(l=Math.floor(r)+" "+s),d>0&&(l=Math.floor(d)+" "+t),l}}const l=function(){let e=null;return function(t){return e||(e=new d(t))}}();t.init=function(e){let t=null,s={};if(null!==e&&"object"==typeof e&&(t=e.hasOwnProperty("dom")?e.dom:null,s=e.hasOwnProperty("lang")&&e.lang.hasOwnProperty("language")?e.lang:{}),t instanceof HTMLElement){if(t.getAttribute("flaskState"))return;t.setAttribute("flaskState","true"),t.addEventListener("click",(()=>l(s).setFlaskStateData()))}else{if(document.getElementById("fs-state-circular"))return;let e="
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e);let t,n=document.getElementById("fs-state-circular");function a(e){n.style.top=e.clientY-t+300+"px"}n.onclick=function(){this.classList.add("fs-circular-out"),l(s).setFlaskStateData()},n.onmousedown=function(e){t=t||e.clientY,document.addEventListener("mousemove",a)},document.onmouseup=function(){document.removeEventListener("mousemove",a);const e=parseInt(n.style.top);n.classList.add("fs-circular-animation"),n.style.top=Math.min(Math.max(e,50),window.screen.height-200)+"px",setTimeout((()=>n.classList.remove("fs-circular-animation")),500)}}}}])})); \ No newline at end of file +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.flaskState=t():e.flaskState=t()}(window,(function(){return function(e){var t={};function s(a){if(t[a])return t[a].exports;var n=t[a]={i:a,l:!1,exports:{}};return e[a].call(n.exports,n,n.exports,s),n.l=!0,n.exports}return s.m=e,s.c=t,s.d=function(e,t,a){s.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:a})},s.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},s.t=function(e,t){if(1&t&&(e=s(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var a=Object.create(null);if(s.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)s.d(a,n,function(t){return e[t]}.bind(null,n));return a},s.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return s.d(t,"a",t),t},s.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},s.p="",s(s.s=0)}([function(e,t,s){"use strict";const a=85,n=75,i=10,o=5,r=1048576;class d{constructor(e){this.language=e,this.mobile=d.isMobile(),this.index=0,this.initFlaskStateContainer(this.mobile),this.setEventListener(),this.initFlaskStateLanguage(this.language),this.setInitParams(),this.mobile&&this.setTagChangeEventListener(this.consoleCpuChart,this.consoleMemoryChart,this.consoleLoadavgChart,this.consoleDiskUsageChart),window.addEventListener("resize",(()=>{d.resizeChartTimer([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])}))}setFlaskStateData(){document.getElementById("fs-background").style.display="block",document.getElementById("fs-info-container").style.display="block",document.getElementsByTagName("body")[0].style.overflowX="hidden",document.getElementsByTagName("body")[0].style.overflowY="hidden",document.getElementById("fs-select-days").value="1",this.setCharts("1")}initFlaskStateContainer(){let e='

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Disk IO
Network IO
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
'+(this.mobile?'
':"
")+"
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e)}setEventListener(){if(window.addEventListener){document.getElementById("fs-info-close").addEventListener("click",(function(){document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out")})),document.getElementById("fs-background").addEventListener("click",(function(e){"fs-background"===String(e.target.id)&&(document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out"))}));let e=document.getElementById("fs-select-days");e.addEventListener("change",(()=>{this.setCharts(e.value)}))}}setTagChangeEventListener(...e){if(document.getElementById("fs-info-tab")){let t=document.getElementById("fs-info-tab").getElementsByTagName("li"),s=document.getElementById("fs-info-tab-memory"),a=t[0],n=0;const i={0:"fs-info-tab-memory",1:"fs-info-tab-cpu",2:"fs-info-network-io",3:"fs-info-tab-loadavg"};for(let o of t){let t=document.getElementById(i[n]);o.children[0].addEventListener("click",(()=>{o.classList.add("active"),a.classList.remove("active"),s.classList.remove("fs-show"),t.classList.add("fs-show"),d.resizeChart(e),s=t,a=o})),n++}}}initFlaskStateLanguage(){0!==Object.keys(this.language).length&&(document.getElementById("fs-host-status-title").innerHTML=this.language.host_status,document.getElementById("fs-redis-status-title").innerHTML=this.language.redis_status,document.getElementById("fs-cpu").innerHTML=this.language.cpu,document.getElementById("fs-memory").innerHTML=this.language.memory,document.getElementById("fs-disk-usage").innerHTML=this.language.disk_usage,document.getElementById("fs-load-avg").innerHTML=this.language.load_avg,document.getElementById("fs-disk-io").innerHTML=this.language.disk_io,document.getElementById("fs-network-io").innerHTML=this.language.network_io,document.getElementById("fs-boot-seconds").innerHTML=this.language.boot_seconds,document.getElementById("fs-used-memory").innerHTML=this.language.used_memory,document.getElementById("fs-used-memory-rss").innerHTML=this.language.used_memory_rss,document.getElementById("fs-mem-fragmentation-ratio").innerHTML=this.language.mem_fragmentation_ratio,document.getElementById("fs-hits-ratio").innerHTML=this.language.hits_ratio,document.getElementById("fs-delta-hits-ratio").innerHTML=this.language.delta_hits_ratio,document.getElementById("fs-uptime-in-seconds").innerHTML=this.language.uptime_in_seconds,document.getElementById("fs-connected-clients").innerHTML=this.language.connected_clients,document.getElementById("fs-days").innerHTML=this.language.days)}setInitParams(){this.consoleCpuChart=echarts.init(document.getElementById("fs-info-cpu-chart"),null,{renderer:"svg"}),this.consoleMemoryChart=echarts.init(document.getElementById("fs-info-memory-chart"),null,{renderer:"svg"}),this.consoleLoadavgChart=echarts.init(document.getElementById("fs-info-loadavg-chart"),null,{renderer:"svg"}),this.consoleDiskUsageChart=echarts.init(document.getElementById("fs-info-networkio-chart"),null,{renderer:"svg"}),this.cpuOption=d.generateChatOption(this.mobile,this.language.cpu||"CPU","",this.language.today||"Today"),this.memoryOption=d.generateChatOption(this.mobile,this.language.memory||"Memory","",this.language.today||"Today"),this.networkIOOption=d.generateChatOption(this.mobile,this.language.network_io||"Network IO","networkIO",this.language.today||"Today"),this.loadavgOption=d.generateChatOption(this.mobile,"Load Avg","loadavg",this.language.minutes||"min")}setCharts(e){this.consoleCpuChart.showLoading(),this.consoleMemoryChart.showLoading(),this.consoleLoadavgChart.showLoading(),this.consoleDiskUsageChart.showLoading(),fetch("/v0/state/hoststatus",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json;charset=UTF-8"},body:JSON.stringify({timeQuantum:e})}).then((e=>{e.ok&&e.json().then((e=>{if(200!==e.code)return;const t=e.data;let s=t.currentStatistic;if(Object.keys(s).length){let e=document.getElementById("fs-host-status").getElementsByClassName("fs-badge-content");e[0].innerHTML=s.memory+"%",e[1].innerHTML=s.cpu+"%",e[2].innerHTML=s.disk_usage+"%",e[3].innerHTML=s.load_avg[0]+","+s.load_avg[1]+","+s.load_avg[2],e[4].innerHTML="R "+d.getFormatBit(s.disk_read)+" | W "+d.getFormatBit(s.disk_write)+"",e[5].innerHTML="⬇ "+d.getFormatBit(s.net_recv)+" | ⬆ "+d.getFormatBit(s.net_sent)+"",e[6].innerHTML=d.getFormatSeconds(s.boot_seconds||0,this.language.days,this.language.hours,this.language.minutes,this.language.seconds);["memory","cpu","disk_usage","load_avg"].forEach((function(t,r){let d;if("load_avg"===t){let e=(s.load_avg[0]+s.load_avg[1]+s.load_avg[2])/3;d=e>=o?e>=i?"background-red":"background-orange":"background-green"}else d=s[t]>=n?s.memory>=a?"background-red":"background-orange":"background-green";let l=e[r].classList;l.remove("background-green","background-orange","background-red"),l.add(d)}));let t=document.getElementById("fs-redis-status").getElementsByClassName("fs-badge-content"),l=["used_memory","used_memory_rss","mem_fragmentation_ratio","hits_ratio","delta_hits_ratio","uptime_in_seconds","connected_clients"],c=!0;for(let e of l)if(s[e]){c=!1;break}c?(document.getElementById("fs-redis-status-title").innerHTML="",document.getElementById("fs-redis-status-title").style.marginTop="0",document.getElementById("fs-redis-status").style.display="none"):l.forEach(((e,a)=>{switch(e){case"used_memory":case"used_memory_rss":t[a].innerHTML=Math.ceil(s[e]/r)+" M";break;case"mem_fragmentation_ratio":let n=s[e];if(t[a].innerHTML=s[e],null!=n&&n>1){let e=t[a].classList;e.remove("background-green"),e.add("background-red")}break;case"hits_ratio":case"delta_hits_ratio":t[a].innerHTML=s[e]+"%";break;case"uptime_in_seconds":t[a].innerHTML=d.getFormatSeconds(s[e],this.language.days,this.language.hours,this.language.minutes,this.language.seconds);break;case"connected_clients":t[a].innerHTML=s[e]}}))}const l=["ts","cpu","memory","load_avg"];t.items=t.items.map((e=>{let t={};return l.forEach(((s,a)=>{if("ts"===s)return t[s]=1e3*e[a];t[s]=e[a]})),t})),t.items.reverse(),t.io.reverse();let c=d.getChartsData(t.items),g=d.getIOChartsData(t.io),u=c.ts_list,m=c.cpu_list,f=c.memory_list,h=c.load_avg_list[0],p=c.load_avg_list[1],y=c.load_avg_list[2];this.networkIOOption.xAxis.data=g.ts_list,this.networkIOOption.series[0].data=g.net_recv,this.networkIOOption.series[1].data=g.net_sent,this.consoleDiskUsageChart.setOption(this.networkIOOption),this.memoryOption.xAxis.data=u,this.cpuOption.xAxis.data=u,this.loadavgOption.xAxis.data=u,this.memoryOption.series[0].data=f,this.cpuOption.series[0].data=m,this.loadavgOption.series[0].data=h,this.loadavgOption.series[1].data=p,this.loadavgOption.series[2].data=y,this.consoleMemoryChart.setOption(this.memoryOption),this.consoleCpuChart.setOption(this.cpuOption),this.consoleLoadavgChart.setOption(this.loadavgOption),d.resizeChart([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])})).then((()=>{this.consoleMemoryChart.hideLoading(),this.consoleCpuChart.hideLoading(),this.consoleLoadavgChart.hideLoading(),this.consoleDiskUsageChart.hideLoading()}))})).catch((function(e){}))}static isMobile(){let e=navigator.userAgent,t={trident:e.indexOf("Trident")>-1,presto:e.indexOf("Presto")>-1,webKit:e.indexOf("AppleWebKit")>-1,gecko:e.indexOf("Gecko")>-1&&-1===e.indexOf("KHTML"),mobile:e.match(/AppleWebKit.*Mobile.*/)&&!0,ios:e.match(/\(i[^;]+;( U;)? CPU.Mac OS X/)&&!0,android:e.indexOf("Android")>-1||e.indexOf("Linux")>-1,iPhone:e.indexOf("iPhone")>-1,iPad:e.indexOf("iPad")>-1,webApp:-1===e.indexOf("Safari"),wechat:e.indexOf("MicroMessenger")>-1,qq:e.match(/\sQQ/i)&&" qq"===e.match(/\sQQ/i)[0]};return t.iPhone||t.iPad||t.webApp||t.wechat||t.qq||t.ios||t.mobile||!1}static resizeChartTimer(e,t){clearTimeout(this.clearId),this.clearId=setTimeout((function(){d.resizeChart(e)}),t||200)}static resizeChart(e){e.forEach((e=>e.resize()))}static generateChatOption(e,t,s="",a=""){let n={color:"loadavg"===s?["#ffa726","#42a5f5","#66bb6a"]:"networkIO"===s?["#ffa726","#42a5f5"]:["#42a5f5"],title:{show:!e,text:t},tooltip:{trigger:"axis",formatter:e=>{let t=echarts.format.formatTime("yyyy-MM-dd hh:mm:ss",new Date(parseInt(e[0].axisValue)),!1)+"
";if("networkIO"===s){for(let s=0;s";return t}for(let a=0;a";return t}},legend:{data:[a],textStyle:{fontSize:14},show:"Load Avg"===t||"networkIO"===s},grid:{left:"3%",right:"4%",bottom:"3%",top:e?30:60,containLabel:!0},toolbox:{show:!e,feature:{saveAsImage:{title:" "}}},xAxis:{type:"category",boundaryGap:!1,axisLabel:{formatter:function(e){return echarts.format.formatTime("hh:mm",new Date(parseInt(e)),!1)}}},yAxis:{type:"value",axisLabel:{formatter:e=>{if("networkIO"===s){let t=d.getFormatBit(e,0);return t.substring(0,t.length-2)}return e+("Load Avg"===t?"":"%")}}},series:[{name:a,type:"line",symbol:"none",hoverAnimation:!1}]};return"loadavg"===s&&(n.legend.data=["1 "+a,"5 "+a,"15 "+a],n.series=[],n.legend.data.forEach((e=>{n.series.push({name:e,type:"line",symbol:"none",hoverAnimation:!1})}))),"networkIO"===s&&(n.legend.data=["recv","sent"],n.series=[],n.legend.data.forEach((e=>{n.series.push({name:e,type:"line",symbol:"none",hoverAnimation:!1})}))),n}static getChartsData(e){let t=[],s=[],a=[],n=[],i=[],o=[];for(let r=0;r=1e12?(a=(a/1e12).toFixed(t),s="TB/s"):a>=1e9?(a=(a/1e9).toFixed(t),s="GB/s"):a>=1e6?(a=(a/1e6).toFixed(t),s="MB/s"):a>=1e3?(a=(a/1e3).toFixed(t),s="KB/s"):(s="B/s",a=a.toFixed(2)),a+" "+s}static getFormatSeconds(e,t="days",s="hours",a="min",n="seconds"){let i=parseInt(e),o=0,r=0,d=0,l="";return i>=60?(o=i/60,o>=60&&(r=o/60,o%=60),r>=24&&(d=r/24,r%=24)):l=i+n,o>0&&(l=Math.floor(o)+" "+a),r>0&&(l=Math.floor(r)+" "+s),d>0&&(l=Math.floor(d)+" "+t),l}}const l=function(){let e=null;return function(t){return e||(e=new d(t))}}();t.init=function(e){let t=null,s={};if(null!==e&&"object"==typeof e&&(t=e.hasOwnProperty("dom")?e.dom:null,s=e.hasOwnProperty("lang")&&e.lang.hasOwnProperty("language")?e.lang:{}),t instanceof HTMLElement){if(t.getAttribute("flaskState"))return;t.setAttribute("flaskState","true"),t.addEventListener("click",(()=>l(s).setFlaskStateData()))}else{if(document.getElementById("fs-state-circular"))return;let e="
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e);let t,n=document.getElementById("fs-state-circular");function a(e){n.style.top=e.clientY-t+300+"px"}n.onclick=function(){this.classList.add("fs-circular-out"),l(s).setFlaskStateData()},n.onmousedown=function(e){t=t||e.clientY,document.addEventListener("mousemove",a)},document.onmouseup=function(){document.removeEventListener("mousemove",a);const e=parseInt(n.style.top);n.classList.add("fs-circular-animation"),n.style.top=Math.min(Math.max(e,50),window.screen.height-200)+"px",setTimeout((()=>n.classList.remove("fs-circular-animation")),500)}}}}])})); \ No newline at end of file From ff01fb8b67e2b2e7da051513dcdb795acb752cc0 Mon Sep 17 00:00:00 2001 From: aaajiulong <525763728@qq.com> Date: Wed, 17 Feb 2021 14:38:24 +0800 Subject: [PATCH 18/20] docs: Modify the display picture --- examples/static/flask_state.png | Bin 42513 -> 37640 bytes 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 examples/static/flask_state.png diff --git a/examples/static/flask_state.png b/examples/static/flask_state.png old mode 100644 new mode 100755 index 625c437b9f0f054b4669bca7d811e7ad41f028db..820490c7a2b94fae6ce633348edee3b8f9b6c6bc GIT binary patch literal 37640 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 literal 42513 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 juhTvVSum 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 From d278cb015b6387dfcd0900e9a9d390bcc86814b7 Mon Sep 17 00:00:00 2001 From: aaajiulong <525763728@qq.com> Date: Wed, 17 Feb 2021 14:45:22 +0800 Subject: [PATCH 19/20] docs: Update README --- README.md | 10 +++++----- README.zh-CN.md | 21 ++++++++++++++++++--- packages/README.md | 9 +++++---- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 12b76d5..d7e0f15 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![Project Logo](https://cdn.jsdelivr.net/gh/yoobool/flask-state@1.0.6/src/flask_state/static/flask_state.png) +![Project Logo](https://cdn.jsdelivr.net/gh/yoobool/flask-state@1.0.7/src/flask_state/static/flask_state.png) [![Contributor Badge](https://img.shields.io/badge/Contributions-Welcome-0059b3)](https://github.com/yoobool/flask-state/tree/master/.github/ISSUE_TEMPLATE) [![Gitter Badge](https://img.shields.io/badge/Chat-Gitter-ff69b4.svg?label=Chat&logo=gitter)](https://gitter.im/flaskstate/community) @@ -11,7 +11,7 @@ English | [简体中文](https://github.com/yoobool/flask-state/blob/master/READ Flask-State is a lightweight chart plugin for displaying machine state data in your web application. -* **Monitored Metric:** CPU, memory, disk usage, LoadAVG and boot time. +* **Monitored Metric:** CPU, memory, disk usage, disk IO, Network IO, LoadAVG and boot time. * **Extensible:** Offers rich customization options, including redis monitoring, user authentication, custom logging, i18n and etc. * **Stable:** Solves multiprocessing concurrency problems (if you use [gunicorn](https://gunicorn.org/)) @@ -20,7 +20,7 @@ built on top of lightweight dependencies. This project is in active development and thoroughly tested to ensure that Flask-State stays up-to-date with its project roadmap. -![Screenshot](https://cdn.jsdelivr.net/gh/yoobool/flask-state@1.0.6/examples/static/flask_state.png) +![Screenshot](https://cdn.jsdelivr.net/gh/yoobool/flask-state@1.0.7/examples/static/flask_state.png) ## Documentation @@ -39,8 +39,8 @@ Alternatively, install Flask-State via NPM or include this script tag to the hea section of your HTML document: ```html - - + + ``` ```bash diff --git a/README.zh-CN.md b/README.zh-CN.md index 0c1d3e4..a2be850 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -1,6 +1,6 @@ [English](https://github.com/yoobool/flask-state/blob/master/README.md) | 简体中文 -![Project Logo](https://cdn.jsdelivr.net/gh/yoobool/flask-state@1.0.6/src/flask_state/static/flask_state.png) +![Project Logo](https://cdn.jsdelivr.net/gh/yoobool/flask-state@1.0.7/src/flask_state/static/flask_state.png) [![Contributor Badge](https://img.shields.io/badge/Contributions-Welcome-0059b3)](https://github.com/yoobool/flask-state/tree/master/.github/ISSUE_TEMPLATE) [![Gitter Badge](https://img.shields.io/badge/Chat-Gitter-ff69b4.svg?label=Chat&logo=gitter)](https://gitter.im/flaskstate/community) @@ -12,7 +12,7 @@ Flask-State是一款在您浏览器上使用的轻便、图表化插件。 -* **监控状态**:CPU,内存,磁盘,LoadAvg,启动时长。 +* **监控状态**:CPU,内存,磁盘,磁盘IO, 网络IO, LoadAvg,启动时长。 * **可扩展**:除记录本机状态外,还包括丰富的扩展功能选择。其中有Redis监控、用户验证、自定义logging和i18n等。 * **稳定**:轻量级的依赖关系,同时解决了多进程并发问题。 @@ -20,7 +20,7 @@ Flask-State是一个活跃的项目,经过了充分的测试以及有一系列 ### -![Screenshot](https://cdn.jsdelivr.net/gh/yoobool/flask-state@1.0.6/examples/static/flask_state.png) +![Screenshot](https://cdn.jsdelivr.net/gh/yoobool/flask-state@1.0.7/examples/static/flask_state.png) ## Documentation @@ -31,6 +31,21 @@ Flask-State是一个活跃的项目,经过了充分的测试以及有一系列 从这里 [PyPI](https://pip.pypa.io/en/stable/quickstart/) 下载: +```bash +pip install Flask-State +``` + +通过NPM安装Flask-State或将此脚本标签放在HTML文件的开头部分: + +```html + + +``` + +```bash +npm install flask-state --save +``` + ## Usage Flask-State插件安装后,还需要引入JavaScript文件和CSS文件,然后初始化组件运行方式。在某些配置上,你也可以选择修改。 diff --git a/packages/README.md b/packages/README.md index 6fb1eb8..f8ecab4 100644 --- a/packages/README.md +++ b/packages/README.md @@ -1,4 +1,4 @@ -![Project Logo](https://cdn.jsdelivr.net/gh/yoobool/flask-state@1.0.6/src/flask_state/static/flask_state.png) +![Project Logo](https://cdn.jsdelivr.net/gh/yoobool/flask-state@1.0.7/src/flask_state/static/flask_state.png) [![Contributor Badge](https://img.shields.io/badge/Contributions-Welcome-0059b3)](https://github.com/yoobool/flask-state/tree/master/.github/ISSUE_TEMPLATE) [![Gitter Badge](https://img.shields.io/badge/Chat-Gitter-ff69b4.svg?label=Chat&logo=gitter)](https://gitter.im/flaskstate/community) @@ -10,7 +10,7 @@ Flask-State is a lightweight chart plugin for displaying machine state data in your web application. -* **Monitored Metric:** CPU, memory, disk usage, LoadAVG and boot time. +* **Monitored Metric:** CPU, memory, disk usage, disk IO, Network IO, LoadAVG and boot time. * **Extensible:** Offers rich customization options, including redis monitoring, user authentication, custom logging, i18n and etc. * **Stable:** Solves multiprocessing concurrency problems (if you use [gunicorn](https://gunicorn.org/)) @@ -19,7 +19,7 @@ built on top of lightweight dependencies. This project is in active development and thoroughly tested to ensure that Flask-State stays up-to-date with its project roadmap. -![Screenshot](https://cdn.jsdelivr.net/gh/yoobool/flask-state@1.0.6/examples/static/flask_state.png) +![Screenshot](https://cdn.jsdelivr.net/gh/yoobool/flask-state@1.0.7/examples/static/flask_state.png) ## Documentation @@ -38,7 +38,8 @@ Alternatively, install Flask-State via NPM or include this script tag to the hea section of your HTML document: ```html - + + ``` ```bash From 1623f6e626770558f2d0c7e8e0e5e5d94761e4f3 Mon Sep 17 00:00:00 2001 From: aaajiulong <525763728@qq.com> Date: Wed, 17 Feb 2021 14:52:05 +0800 Subject: [PATCH 20/20] chore: Update version info --- examples/templates/index.html | 16 ++++++---------- package.json | 2 +- packages/package.json | 2 +- src/flask_state/__about__.py | 2 +- 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/examples/templates/index.html b/examples/templates/index.html index f460d6f..3a608be 100644 --- a/examples/templates/index.html +++ b/examples/templates/index.html @@ -5,21 +5,17 @@ index - - + + + -
+
click me
+ - \ No newline at end of file + diff --git a/package.json b/package.json index 586c402..bde8ff2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flask-state", - "version": "1.0.6", + "version": "1.0.7", "description": "Flask-States is a visual plug-in based on flask. It can record the local state every minute and read the status of redis if you have configured redis,and generate data chart to show to users through Echarts.", "main": "./packages/index.js", "directories": { diff --git a/packages/package.json b/packages/package.json index dded41e..23d35e8 100644 --- a/packages/package.json +++ b/packages/package.json @@ -1,6 +1,6 @@ { "name": "flask-state", - "version": "1.0.6", + "version": "1.0.7", "description": "Flask-States is a visual plug-in based on flask. It can record the local state every minute and read the status of redis if you have configured redis,and generate data chart to show to users through Echarts.", "main": "index.js", "scripts": { diff --git a/src/flask_state/__about__.py b/src/flask_state/__about__.py index 382021f..9e604c0 100644 --- a/src/flask_state/__about__.py +++ b/src/flask_state/__about__.py @@ -1 +1 @@ -__version__ = "1.0.6" +__version__ = "1.0.7"