Skip to content

Commit

Permalink
v1.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
unknown committed Nov 5, 2024
1 parent 4641520 commit ca046d9
Show file tree
Hide file tree
Showing 11 changed files with 1,100 additions and 1 deletion.
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,15 @@
# asitop-exporter
# asitop-exporter
参考 nvitop-exporter,基于 asitop 开发的 Mac 系统上metrics监控工具
## 一、编译
pip install prometheus_client Cython pyinstaller requests
### whl
python setup.py bdist_wheel
### 可执行文件
cd asitop_exporter
pyinstaller __main__.spec
## 二、使用
sudo asitop-exporter -B 10.20.30.40 -p 9999 --interval 60.0 --post_url http://xxx.xxx.xxx.xxx/
1、程序需要以 sudo 权限运行
2、程序运行 10.20.30.40 的 9999 端口, 可以通过http请求 http://10.20.30.40:9999/metrics 获取 prometheus 格式的信息
3、--interval 60.0 监控间隔,表示每60s获取一次信息,默认是5s
4、--post_url http://xxx.xxx.xxx.xxx/ 监控信息回调接口,以json格式返回。不设置则不会post.
24 changes: 24 additions & 0 deletions asitop_exporter/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# This file is part of asitop, the interactive NVIDIA-GPU process viewer.
#
# Copyright 2021-2024 fangxuwei. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Prometheus exporter built on top of ``asitop``."""

from asitop_exporter.exporter import PrometheusExporter
from asitop_exporter.utils import get_ip_address
from asitop_exporter.version import __version__


__all__ = ['PrometheusExporter', 'get_ip_address']
25 changes: 25 additions & 0 deletions asitop_exporter/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# This file is part of asitop, the interactive NVIDIA-GPU process viewer.
#
# Copyright 2021-2024 fangxuwei. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Prometheus exporter built on top of ``asitop``."""

import sys

from asitop_exporter.cli import main


if __name__ == '__main__':
sys.exit(main())
38 changes: 38 additions & 0 deletions asitop_exporter/__main__.spec
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# -*- mode: python ; coding: utf-8 -*-


a = Analysis(
['__main__.py'],
pathex=[],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)

exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name='asitop-exporter',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
220 changes: 220 additions & 0 deletions asitop_exporter/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
# This file is part of asitop, the interactive NVIDIA-GPU process viewer.
#
# Copyright 2021-2024 fangxuwei. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Prometheus exporter built on top of ``asitop``."""

from __future__ import annotations
import time
import argparse
import sys
from typing import TextIO

from prometheus_client import start_wsgi_server
from termcolor import colored

from asitop_exporter.exporter import PrometheusExporter
from asitop_exporter.utils import get_ip_address, run_powermetrics_process
from asitop_exporter.version import __version__


def cprint(text: str = '', *, file: TextIO | None = None) -> None:
"""Print colored text to a file."""
for prefix, color in (
('INFO: ', 'yellow'),
('WARNING: ', 'yellow'),
('ERROR: ', 'red'),
('NVML ERROR: ', 'red'),
):
if text.startswith(prefix):
# text = text.replace(
# prefix.rstrip(),
# colored(prefix.rstrip(), color=color, attrs=('bold',)),
# 1,
# )
text = prefix.rstrip()
print(text, file=file)


def parse_arguments() -> argparse.Namespace:
"""Parse command-line arguments for ``asitop-exporter``."""

def posfloat(argstring: str) -> float:
num = float(argstring)
if num <= 0:
raise ValueError
return num

posfloat.__name__ = 'positive float'

parser = argparse.ArgumentParser(
prog='asitop-exporter',
description='Prometheus exporter built on top of `asitop`.',
formatter_class=argparse.RawTextHelpFormatter,
add_help=False,
)
parser.add_argument(
'--help',
'-h',
dest='help',
action='help',
default=argparse.SUPPRESS,
help='Show this help message and exit.',
)
parser.add_argument(
'--version',
'-V',
dest='version',
action='version',
version=f'%(prog)s {__version__} (asitop {__version__})',
help="Show %(prog)s's version number and exit.",
)

parser.add_argument(
'--hostname',
'--host',
'-H',
dest='hostname',
type=str,
default=get_ip_address(),
metavar='HOSTNAME',
help='Hostname to display in the exporter. (default: %(default)s)',
)
parser.add_argument(
'--bind-address',
'--bind',
'-B',
dest='bind_address',
type=str,
default='127.0.0.1',
metavar='ADDRESS',
help='Local address to bind to. (default: %(default)s)',
)
parser.add_argument(
'--port',
'-p',
type=int,
default=8000,
help='Port to listen on. (default: %(default)d)',
)
parser.add_argument(
'--interval',
dest='interval',
type=posfloat,
default=5.0,
metavar='SEC',
help='Interval between updates in seconds. (default: %(default)s)',
)

parser.add_argument(
'--post_url',
dest='post_url',
type=str,
default=None,
metavar='ADDRESS',
help='post result to url',
)

parser.add_argument(
'--alive_time',
dest='alive_time',
type=int,
default=60,
metavar='minute',
help='powermetrics file alive time. asitop-exporter will delete the pre file and create a new one after alive_time',
)

args = parser.parse_args()
if args.interval < 0.25:
parser.error(
f'the interval {args.interval:0.2g}s is too short, which may cause performance issues. '
f'Expected 1/4 or higher.',
)

return args


def main() -> int: # pylint: disable=too-many-locals,too-many-statements
"""Main function for ``asitop-exporter`` CLI."""
args = parse_arguments()


timecode = str(int(time.time()))

# powermetrics_process = run_powermetrics_process(timecode,
# interval=args.interval * 1000)

exporter = PrometheusExporter(hostname=args.hostname, interval=args.interval, timecode=timecode, post_url=args.post_url, alive_time=args.alive_time)
exporter.start_powermetrics_process()

try:
start_wsgi_server(port=args.port, addr=args.bind_address)
except OSError as ex:
if 'address already in use' in str(ex).lower():
cprint(
(
'ERROR: Address {} is already in use. '
'Please specify a different port via `--port <PORT>`.'
).format(
colored(
f'http://{args.bind_address}:{args.port}',
color='blue',
attrs=('bold', 'underline'),
),
),
file=sys.stderr,
)
elif 'cannot assign requested address' in str(ex).lower():
cprint(
(
'ERROR: Cannot assign requested address at {}. '
'Please specify a different address via `--bind-address <ADDRESS>`.'
).format(
colored(
f'http://{args.bind_address}:{args.port}',
color='blue',
attrs=('bold', 'underline'),
),
),
file=sys.stderr,
)
else:
cprint(f'ERROR: {ex}', file=sys.stderr)
return 1

cprint(
'INFO : Start the exporter on {} at {}.'.format(
colored(args.hostname, color='magenta', attrs=('bold',)),
colored(
f'http://{args.bind_address}:{args.port}/metrics',
color='green',
attrs=('bold', 'underline'),
),
),
file=sys.stderr,
)

try:
exporter.collect()
except KeyboardInterrupt:
cprint(file=sys.stderr)
cprint('INFO: Interrupted by user.', file=sys.stderr)
exporter.terminate_powermetrics_process()

return 0


if __name__ == '__main__':
sys.exit(main())
Loading

0 comments on commit ca046d9

Please sign in to comment.