From 024c1e25c0ee0858b0e77058c0d7613eb785a7b7 Mon Sep 17 00:00:00 2001 From: Wongkraiwich Chuenchomphu Date: Tue, 30 Jul 2024 00:14:55 +0700 Subject: [PATCH] v1.0.0 --- README.md | 92 ++++++++++++++++++++ dist/mtnlog-1.0.0-py3-none-any.whl | Bin 0 -> 3870 bytes dist/mtnlog-1.0.0.tar.gz | Bin 0 -> 3393 bytes pyproject.toml | 22 +++++ src/mtnlog.egg-info/PKG-INFO | 105 +++++++++++++++++++++++ src/mtnlog.egg-info/SOURCES.txt | 9 ++ src/mtnlog.egg-info/dependency_links.txt | 1 + src/mtnlog.egg-info/top_level.txt | 1 + src/mtnlog/__init__.py | 11 +++ src/mtnlog/json.py | 28 ++++++ src/mtnlog/performance.py | 81 +++++++++++++++++ 11 files changed, 350 insertions(+) create mode 100644 README.md create mode 100644 dist/mtnlog-1.0.0-py3-none-any.whl create mode 100644 dist/mtnlog-1.0.0.tar.gz create mode 100644 pyproject.toml create mode 100644 src/mtnlog.egg-info/PKG-INFO create mode 100644 src/mtnlog.egg-info/SOURCES.txt create mode 100644 src/mtnlog.egg-info/dependency_links.txt create mode 100644 src/mtnlog.egg-info/top_level.txt create mode 100644 src/mtnlog/__init__.py create mode 100644 src/mtnlog/json.py create mode 100644 src/mtnlog/performance.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..9ea5946 --- /dev/null +++ b/README.md @@ -0,0 +1,92 @@ +# mtnlog - A simple multinode performance logger for Python + +## Introduction + +mtnlog is a simple multinode performance logger for Python. It is designed to be used in a similar way to Python's built-in logging module, but with a focus on performance logging. It provides a simple API for logging performance data, including start and end times, and allows for easy integration with other logging systems. + +## Installation + +You can install mtnlog using pip: + +```bash +pip install mtnlog +``` + +## Usage + +To use mtnlog, you have two features: `JSONLogger` and `PerformanceLogger`. + +### JSONLogger + +The `JSONLogger` class is a simple logger that writes performance data to a JSON file. You can create a new `JSONLogger` instance by passing a file path to the constructor: + +```python +from mtnlog import JSONLogger + +logger = JSONLogger(log_dir='logs') # logs is the directory where the log file will be saved +``` + +You can then use the `log` method to log performance data: + +```python +logger.log('', filename='log') # your_dict is a dictionary with the data you want to log / filename is the name of the file +``` + +`your_dict` is a dictionary with the data you want to log. +`filename` is the name of the file where the data will be saved + +### PerformanceLogger + +The `PerformanceLogger` class is a logger for system performance data. It logs the the time taken to execute the block, as well as the CPU, memory, and GPU usage. You can create a new `PerformanceLogger` instance by passing a file path to the constructor: + +```python +from mtnlog import PerformanceLogger + +collector = PerformanceLogger(log_dir="", log_node="") +``` + +`your_log_dir` is the directory where the log file will be saved. +`current_node` is the number of the node you are logging. + +You can then use the `change_tag` method to change the tag of the log: + +```python +collector.change_tag("") +``` + +`new_tag` is the new tag you want to use. + +To stop logging, you can use the `stop` method: + +```python +collector.stop() +``` + +## Example + +Here is an example of how to use mtnlog: + +```python +from mtnlog import JSONLogger, PerformanceLogger + +# Create a JSONLogger instance + +logger = JSONLogger(log_dir='logs') + +# Log some data + +logger.log({'message': 'Hello, world!'}, filename='log') + +# Create a PerformanceLogger instance + +collector = PerformanceLogger(log_dir='logs', log_node="0") + +# Change the tag + +collector.change_tag('new_tag') + +# Stop logging + +collector.stop() + +``` \ No newline at end of file diff --git a/dist/mtnlog-1.0.0-py3-none-any.whl b/dist/mtnlog-1.0.0-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..a735c8941dddedf9086d55a52c44b4e1ca779ba3 GIT binary patch literal 3870 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 fzr^~W;9nE}l#;Fn3Fwc;4S)t~t*Q2K+UR-$e> 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 X(M1RM*~t literal 0 HcmV?d00001 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..5eee672 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,22 @@ +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" + +[project] +name = "mtnlog" +version = "1.0.0" +authors = [ + { name = "Wongkraiwich Chuenchomphu", email = "wongkraiwich@inedible.dev" }, +] +description = "A simple performance logger for Python" +readme = "README.md" +requires-python = ">=3.8" +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", +] + +[project.urls] +Homepage = "https://github.com/kentakoong/mtnlog" +Issues = "https://github.com/kentakoong/mtnlog/issues" diff --git a/src/mtnlog.egg-info/PKG-INFO b/src/mtnlog.egg-info/PKG-INFO new file mode 100644 index 0000000..eb175c6 --- /dev/null +++ b/src/mtnlog.egg-info/PKG-INFO @@ -0,0 +1,105 @@ +Metadata-Version: 2.1 +Name: mtnlog +Version: 1.0.0 +Summary: A simple performance logger for Python +Author-email: Wongkraiwich Chuenchomphu +Project-URL: Homepage, https://github.com/kentakoong/mtnlog +Project-URL: Issues, https://github.com/kentakoong/mtnlog/issues +Classifier: Programming Language :: Python :: 3 +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Requires-Python: >=3.8 +Description-Content-Type: text/markdown + +# mtnlog - A simple multinode performance logger for Python + +## Introduction + +mtnlog is a simple multinode performance logger for Python. It is designed to be used in a similar way to Python's built-in logging module, but with a focus on performance logging. It provides a simple API for logging performance data, including start and end times, and allows for easy integration with other logging systems. + +## Installation + +You can install mtnlog using pip: + +```bash +pip install mtnlog +``` + +## Usage + +To use mtnlog, you have two features: `JSONLogger` and `PerformanceLogger`. + +### JSONLogger + +The `JSONLogger` class is a simple logger that writes performance data to a JSON file. You can create a new `JSONLogger` instance by passing a file path to the constructor: + +```python +from mtnlog import JSONLogger + +logger = JSONLogger(log_dir='logs') # logs is the directory where the log file will be saved +``` + +You can then use the `log` method to log performance data: + +```python +logger.log('', filename='log') # your_dict is a dictionary with the data you want to log / filename is the name of the file +``` + +`your_dict` is a dictionary with the data you want to log. +`filename` is the name of the file where the data will be saved + +### PerformanceLogger + +The `PerformanceLogger` class is a logger for system performance data. It logs the the time taken to execute the block, as well as the CPU, memory, and GPU usage. You can create a new `PerformanceLogger` instance by passing a file path to the constructor: + +```python +from mtnlog import PerformanceLogger + +collector = PerformanceLogger(log_dir="", log_node="") +``` + +`your_log_dir` is the directory where the log file will be saved. +`current_node` is the number of the node you are logging. + +You can then use the `change_tag` method to change the tag of the log: + +```python +collector.change_tag("") +``` + +`new_tag` is the new tag you want to use. + +To stop logging, you can use the `stop` method: + +```python +collector.stop() +``` + +## Example + +Here is an example of how to use mtnlog: + +```python +from mtnlog import JSONLogger, PerformanceLogger + +# Create a JSONLogger instance + +logger = JSONLogger(log_dir='logs') + +# Log some data + +logger.log({'message': 'Hello, world!'}, filename='log') + +# Create a PerformanceLogger instance + +collector = PerformanceLogger(log_dir='logs', log_node="0") + +# Change the tag + +collector.change_tag('new_tag') + +# Stop logging + +collector.stop() + +``` diff --git a/src/mtnlog.egg-info/SOURCES.txt b/src/mtnlog.egg-info/SOURCES.txt new file mode 100644 index 0000000..4ce4f84 --- /dev/null +++ b/src/mtnlog.egg-info/SOURCES.txt @@ -0,0 +1,9 @@ +README.md +pyproject.toml +src/mtnlog/__init__.py +src/mtnlog/json.py +src/mtnlog/performance.py +src/mtnlog.egg-info/PKG-INFO +src/mtnlog.egg-info/SOURCES.txt +src/mtnlog.egg-info/dependency_links.txt +src/mtnlog.egg-info/top_level.txt \ No newline at end of file diff --git a/src/mtnlog.egg-info/dependency_links.txt b/src/mtnlog.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/mtnlog.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/src/mtnlog.egg-info/top_level.txt b/src/mtnlog.egg-info/top_level.txt new file mode 100644 index 0000000..c2c4484 --- /dev/null +++ b/src/mtnlog.egg-info/top_level.txt @@ -0,0 +1 @@ +mtnlog diff --git a/src/mtnlog/__init__.py b/src/mtnlog/__init__.py new file mode 100644 index 0000000..156360d --- /dev/null +++ b/src/mtnlog/__init__.py @@ -0,0 +1,11 @@ +from .json import * +from .performance import * + +__version__ = '1.0.0' + +__doc__ = """Performance logger for tracking resource usage.""" + +__all__ = [ + 'JSONLogger', + 'PerformanceLogger', +] diff --git a/src/mtnlog/json.py b/src/mtnlog/json.py new file mode 100644 index 0000000..114fea5 --- /dev/null +++ b/src/mtnlog/json.py @@ -0,0 +1,28 @@ +"""JSON logger class for logging arguments and results.""" + +import json +import os + + +class JSONLogger: + """Arguments logger class.""" + + def __init__(self, log_dir): + os.makedirs(log_dir, exist_ok=True) + self.log_dir = log_dir + + def serialize(self, obj): + """Custom serialization for non-serializable objects.""" + if isinstance(obj, dict): + return {k: self.serialize(v) for k, v in obj.items()} + if isinstance(obj, list): + return [self.serialize(v) for v in obj] + if isinstance(obj, (int, float, str, bool, type(None))): + return obj + # Convert non-serializable objects to their string representation + return str(obj) + + def log(self, obj, filename="log"): + """Logs the object.""" + with open(f"{self.log_dir}/{filename}.json", "w", encoding='utf-8') as f: + json.dump(self.serialize(obj), f, ensure_ascii=False, indent=4) diff --git a/src/mtnlog/performance.py b/src/mtnlog/performance.py new file mode 100644 index 0000000..b360fcc --- /dev/null +++ b/src/mtnlog/performance.py @@ -0,0 +1,81 @@ +"""Performance logger class for logging performance metrics.""" + +import os +import psutil + +import pandas as pd +from nvitop import ResourceMetricCollector, Device + + +class PerformanceLogger: + """Performance logger class.""" + + def __init__(self, log_dir, log_node): + + os.makedirs(log_dir, exist_ok=True) + + self.log_dir = log_dir + self.log_node = log_node + self.df = pd.DataFrame() + self.tag = None + self.filepath = None + self.collector = ResourceMetricCollector(Device.cuda.all()).daemonize( + on_collect=self.on_collect, + interval=1.0, + ) + self.cpu_count = psutil.cpu_count(logical=False) + self.start_time = None + + def new_res(self): + """Returns the directory.""" + + os.makedirs(f"{self.log_dir}/{self.tag}", exist_ok=True) + + self.filepath = f"{self.log_dir}/{self.tag}/node-{self.log_node}.csv" + + def change_tag(self, tag): + """Changes the tag.""" + if self.filepath is not None: + self.stop() + self.tag = tag + self.new_res() + + def stop(self): + """Stops the collector.""" + if not self.df.empty: + self.df.to_csv(self.filepath, index=False) + self.df = pd.DataFrame() + + def get_cpu_usage_per_core(self): + """Returns the CPU usage per core.""" + cpu_percent = psutil.cpu_percent(interval=0.1, percpu=True) + return {f"cpu_core_{i+1}": percent for i, percent in enumerate(cpu_percent[:self.cpu_count])} + + def clean_column_name(self, col): + """Cleans the column name.""" + if col.startswith("metrics-daemon/host/"): + col = col[len("metrics-daemon/host/"):] + return col + + def on_collect(self, metrics): + """Collects metrics.""" + + metrics['tag'] = self.tag + + cpu_metrics = self.get_cpu_usage_per_core() + metrics.update(cpu_metrics) + + df_metrics = pd.DataFrame.from_records([metrics]) + + df_metrics.columns = [self.clean_column_name(col) for col in df_metrics.columns] + + if self.df.empty: + self.df = df_metrics + else: + for col in df_metrics.columns: + if col not in self.df.columns: + self.df[col] = None + + self.df = pd.concat([self.df, df_metrics], ignore_index=True) + + return True