├── .codespellrc ├── .coveragerc ├── .editorconfig ├── .flake8 ├── .github ├── dependabot.yml └── workflows │ ├── lint.yml │ ├── publish.yml │ └── tests.yml ├── .gitignore ├── .pre-commit-config.yaml ├── AUTHORS.md ├── CHANGELOG.md ├── LICENSE.txt ├── MANIFEST.in ├── README.md ├── RELEASE.md ├── docs ├── imgs │ ├── logo-horizontal.png │ ├── logo-horizontal.svg │ ├── logo.svg │ └── screenshot.png └── man │ ├── build-man.sh │ ├── pg_activity.1 │ └── pg_activity.pod ├── mypy.ini ├── pgactivity ├── __init__.py ├── __main__.py ├── activities.py ├── cli.py ├── colors.py ├── compat.py ├── config.py ├── data.py ├── handlers.py ├── keys.py ├── pg.py ├── profiles │ ├── minimal.conf │ ├── narrow.conf │ └── wide.conf ├── queries │ ├── __init__.py │ ├── disable_log_min_duration_sample.sql │ ├── disable_log_min_duration_statement.sql │ ├── disable_log_statement.sql │ ├── do_pg_cancel_backend.sql │ ├── do_pg_terminate_backend.sql │ ├── get_blocking_oldest.sql │ ├── get_blocking_post_090200.sql │ ├── get_blocking_post_090600.sql │ ├── get_data_directory.sql │ ├── get_pg_activity_oldest.sql │ ├── get_pg_activity_post_090200.sql │ ├── get_pg_activity_post_090400.sql │ ├── get_pg_activity_post_090600.sql │ ├── get_pg_activity_post_100000.sql │ ├── get_pg_activity_post_110000.sql │ ├── get_pg_activity_post_130000.sql │ ├── get_pga_inet_addresses.sql │ ├── get_replication_slots_post_140000.sql │ ├── get_server_info_oldest.sql │ ├── get_server_info_post_090100.sql │ ├── get_server_info_post_090200.sql │ ├── get_server_info_post_090400.sql │ ├── get_server_info_post_090600.sql │ ├── get_server_info_post_100000.sql │ ├── get_server_info_post_110000.sql │ ├── get_temporary_files_oldest.sql │ ├── get_temporary_files_post_090100.sql │ ├── get_temporary_files_post_090500.sql │ ├── get_temporary_files_post_120000.sql │ ├── get_version.sql │ ├── get_waiting_oldest.sql │ ├── get_waiting_post_090200.sql │ ├── get_wal_receivers_post_090600.sql │ ├── get_wal_senders_post_090100.sql │ └── reset_statement_timeout.sql ├── types.py ├── ui.py ├── utils.py ├── views.py └── widgets.py ├── pyproject.toml ├── pytest.ini ├── setup.py ├── tests ├── conftest.py ├── data │ └── local-processes-input.json ├── test_activities.py ├── test_cli.py ├── test_cli_help.txt ├── test_cli_help_py312.txt ├── test_config.py ├── test_data.py ├── test_scroll.txt ├── test_types.py ├── test_ui.txt ├── test_views.py ├── test_views.txt └── test_widgets.txt └── tox.ini /.codespellrc: -------------------------------------------------------------------------------- 1 | [codespell] 2 | skip = .git,.mypy_cache,.tox,.venv,build,htmlcov 3 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | include = 3 | pg_activity 4 | pgactivity/* 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.md] 4 | max_line_length = 80 5 | 6 | [LICENSE.txt] 7 | max_line_length = 78 8 | 9 | [*.py] 10 | max_line_length = 89 11 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | doctests = True 3 | ignore = 4 | # whitespace before ‘:’ 5 | E203, 6 | # line too long 7 | E501, 8 | # multiple statements on one line (def) 9 | E704, 10 | # line break before binary operator 11 | W503, 12 | exclude = 13 | .git, 14 | .mypy_cache, 15 | .tox, 16 | .venv, 17 | per-file-ignores = 18 | pg_activity:E251 19 | setup.py:E251,E402 20 | select = B,C,E,F,W,T4,B9 21 | mypy_config = mypy.ini 22 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 2 | 3 | version: 2 4 | updates: 5 | - package-ecosystem: "github-actions" 6 | directory: "/" 7 | schedule: 8 | interval: "monthly" 9 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | lint: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: actions/setup-python@v5 11 | - name: Install tox 12 | run: pip install tox 13 | - name: Check manifest 14 | run: tox -e check-manifest 15 | - name: Lint 16 | run: tox -e lint 17 | - name: Mypy 18 | run: tox -e mypy 19 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | jobs: 9 | publish: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: actions/setup-python@v5 15 | with: 16 | python-version: '3.11' 17 | - name: Install 18 | run: python -m pip install build setuptools twine wheel 19 | - name: Build 20 | run: | 21 | python -m build 22 | python -m twine check dist/* 23 | - name: Publish 24 | run: python -m twine upload dist/* 25 | env: 26 | TWINE_USERNAME: __token__ 27 | TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} 28 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | tests: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | include: 11 | - python: "3.9" 12 | psycopg: "psycopg2" 13 | - python: "3.13" 14 | psycopg: "psycopg3" 15 | steps: 16 | - uses: actions/checkout@v4 17 | - name: Setup Python 18 | uses: actions/setup-python@v5 19 | with: 20 | python-version: ${{ matrix.python }} 21 | allow-prereleases: true 22 | - name: Install tox 23 | run: pip install tox 24 | - name: Add fr_FR and zh_TW for test purposes 25 | run: | 26 | sudo locale-gen fr_FR zh_TW.EUC-TW 27 | sudo update-locale 28 | - name: Test 29 | run: tox -e py-${{ matrix.psycopg }} 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .coverage 2 | .tox 3 | .vscode 4 | __pycache__/ 5 | build/ 6 | dist/ 7 | htmlcov/ 8 | pg_activity.egg-info 9 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v4.4.0 4 | hooks: 5 | - id: check-added-large-files 6 | - id: debug-statements 7 | - id: end-of-file-fixer 8 | files: text 9 | exclude: tests/test_*.txt 10 | - id: trailing-whitespace 11 | files: text 12 | exclude: tests/test_*.txt 13 | - repo: local 14 | hooks: 15 | - id: pyupgrade 16 | name: pyupgrade 17 | entry: pyupgrade --py39-plus --exit-zero-even-if-changed 18 | language: system 19 | types: [python] 20 | - id: black 21 | name: black 22 | entry: black --check . 23 | language: system 24 | types: [python] 25 | - id: flake8 26 | name: flake8 27 | entry: flake8 . 28 | language: system 29 | types: [python] 30 | - id: isort 31 | name: isort 32 | entry: isort --check --diff . 33 | language: system 34 | types: [python] 35 | - id: mypy 36 | name: mypy 37 | entry: mypy 38 | language: system 39 | types: [python] 40 | exclude: tests 41 | - id: codespell 42 | name: codespell 43 | entry: codespell 44 | language: system 45 | types: [file] 46 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | # pg_activity Development Team 2 | 3 | pg_activity is an open project. Feel free to join us and improve this tool. 4 | 5 | 6 | ## Maintainers 7 | 8 | - Benoit Lobréau 9 | - Denis Laxalde 10 | 11 | 12 | ## Contributors 13 | 14 | * Julien Tachoires - creator and original maintainer. 15 | * [Damien Cazeils](https://www.damiencazeils.com/) - logo 16 | * Fabrízio de Royes Mello 17 | * crisnamurti 18 | * Étienne BERSAC 19 | * Michel Milezzi 20 | * Vincent Maugé 21 | * Kaarel Moppel 22 | * Alexandre Fayolle 23 | * Fabio Renato Geiss 24 | * Julien Rouhaud 25 | * MattK 26 | * Nick LaMuro 27 | * Nicolas Seinlet 28 | * Andrey Zhidenkov 29 | * Christophe Courtois 30 | * Damien Garaud 31 | * Emmanuel Bouthenot 32 | * Feike Steenbergen 33 | * Marion Delcambre 34 | * Matheus de Oliveira 35 | * Mickaël Le Baillif 36 | * Nicolas Dandrimont 37 | * Nils Hamerlinck 38 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 - 2019, Julien Tachoires 2 | Copyright (c) 2020, Dalibo 3 | 4 | Permission to use, copy, modify, and distribute this software and its 5 | documentation for any purpose, without fee, and without a written agreement is 6 | hereby granted, provided that the above copyright notice and this paragraph 7 | and the following two paragraphs appear in all copies. 8 | 9 | IN NO EVENT SHALL DALIBO BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, 10 | INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF 11 | THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF DALIBO HAS BEEN 12 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | 14 | DALIBO SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 | PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND DALIBO 17 | HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR 18 | MODIFICATIONS. 19 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include docs/man/pg_activity.1 2 | recursive-include pgactivity *.py *.sql *.conf 3 | recursive-include docs *.png *.pod *.sh *.svg 4 | recursive-include tests *.py *.json *.txt 5 | include *.md 6 | include *.txt 7 | include .editorconfig 8 | include .codespellrc 9 | include .coveragerc 10 | include .flake8 11 | include .pre-commit-config.yaml 12 | include mypy.ini 13 | include pyproject.toml 14 | include pytest.ini 15 | include tox.ini 16 | 17 | prune .github 18 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Release HOW TO 2 | 3 | ## Preparatory changes 4 | 5 | * Review the **Unreleased** section, if any, in `CHANGELOG.md` possibly adding 6 | any missing item from closed issues, merged pull requests, or directly the git 7 | history[^git-changes], 8 | * Rename the **Unreleased** section according to the version to be released, 9 | with a date, 10 | * Bump the version in `pgactivity/__init__.py`, 11 | * Rebuild the man page, and, 12 | * Commit these changes (either on a dedicated branch, before submitting a pull 13 | request or directly on the `master` branch). 14 | * Then, when changes landed in the `master` branch, create an annotated (and 15 | possibly signed) tag, as `git tag -a [-s] -m 'pg_activity 1.6.0' v1.6.0`, and, 16 | * Push with `--follow-tags`. 17 | 18 | [^git-changes]: Use `git log $(git describe --tags --abbrev=0).. --format=%s 19 | --reverse` to get commits from the previous tag. 20 | 21 | ## PyPI package 22 | 23 | This requires no special action as, upon push of a tag on GitHub, the "publish" 24 | workflow will build the Python package and upload to PyPI. 25 | 26 | ## GitHub release 27 | 28 | *Draft a new release* from [release page][], choosing the tag just pushed and 29 | copy respective change log section as a description. 30 | 31 | [release page]: https://github.com/dalibo/pg_activity/releases 32 | 33 | ## Create a news article on postgresql.org and submit it 34 | 35 | Example for release 1.6.0 : 36 | ``` 37 | pg_activity (https://github.com/dalibo/pg_activity) 1.6.0 has been released. 38 | 39 | This release adds the following features : 40 | 41 | * the --min-duration flag to only show laggy queries (kmoppel) 42 | * the --duration-mode and the shortcut (T) to choose from the duration modes: 43 | query, transaction, backend (nilshamerlinck) 44 | * the D shortcut to refresh dbsize (Fabio Renato Geiss) 45 | * an expanded refresh interval from 1-3s to 0.5-5s (kmoppel) 46 | 47 | The full release notes can be read here : 48 | https://github.com/dalibo/pg_activity/releases/tag/v1.6.0 49 | -------------------------------------------------------------------------------- /docs/imgs/logo-horizontal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dalibo/pg_activity/643c1f1ed2f4250e640e790d1ba5977774e98860/docs/imgs/logo-horizontal.png -------------------------------------------------------------------------------- /docs/imgs/logo-horizontal.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xmlpg_activity 98 | -------------------------------------------------------------------------------- /docs/imgs/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml -------------------------------------------------------------------------------- /docs/imgs/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dalibo/pg_activity/643c1f1ed2f4250e640e790d1ba5977774e98860/docs/imgs/screenshot.png -------------------------------------------------------------------------------- /docs/man/build-man.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | pod2man \ 4 | -r "$(pg_activity --version)" \ 5 | -d "$(date +%Y-%m-%d)" \ 6 | -c "Command line tool for PostgreSQL server activity monitoring." \ 7 | pg_activity.pod > pg_activity.1 8 | -------------------------------------------------------------------------------- /docs/man/pg_activity.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | pg_activity - Realtime PostgreSQL database server monitoring tool 4 | 5 | =head1 SYNOPSIS 6 | 7 | B [option..] [connection string] 8 | 9 | =head1 DESCRIPTION 10 | 11 | Command line tool for PostgreSQL server activity monitoring. 12 | 13 | pg_activity must run on the same server as the instance and 14 | as the user running the instance (or root) to show 15 | CPU, MEM, READ or WRITE columns and other system information. 16 | 17 | =head2 THE HEADER 18 | 19 | The first line of the header displays PostgreSQL's version, the host name, the 20 | connection string, the refresh rate and the duration mode. 21 | 22 | The header is then divided in tree groups: B, B, 23 | B. The information is filtered according to the filter parameters when 24 | appropriate. This is shown in the following descriptions with the "(filtered)" 25 | mention. Depending on the version you are on, some information might not be 26 | available. In that case, it will be replaced by a dash. 27 | 28 | The B group displays information aubout the PostgreSQL instance (or 29 | cluster). This group can be displayed or hidden with the I Key. 30 | 31 | Global : 32 | 33 | =over 2 34 | 35 | =item - B: since when is the instance running; 36 | 37 | =item - B: total size of the databases (filtered); 38 | 39 | =item - B: growth in B/s of the databases (filtered); 40 | 41 | =item - B: the percentage of page read from the PostgreSQL's cache since last snapshot (filtered). 42 | 43 | =back 44 | 45 | Sessions : 46 | 47 | =over 2 48 | 49 | =item - B: session count (filtered) / max_connections; 50 | 51 | =item - B: number of active sessions (filtered); 52 | 53 | =item - B: number of idle sessions (filtered); 54 | 55 | =item - B: number of sessions who are in the idle in transaction state (filtered); 56 | 57 | =item - B: number of sessions who are in the idle in transaction aborted state (filtered); 58 | 59 | =item - B: number of sessions that are waiting for a lock (filtered). 60 | 61 | =back 62 | 63 | Activity : 64 | 65 | =over 2 66 | 67 | =item - B: transaction per second (sum of commit & rollback for all databases / time elapsed since last snapshot) (filtered); 68 | 69 | =item - B: number of inserts per second (filtered); 70 | 71 | =item - B: number of updates per second (filtered); 72 | 73 | =item - B: number of deletes per second (filtered); 74 | 75 | =item - B: number of tuples returned per second (filtered); 76 | 77 | =item - B: number of temporary files created on the instance; 78 | 79 | =item - B: total temporary file size on the instance. 80 | 81 | =back 82 | 83 | The B group displays information about backgroup workers, 84 | autovacuum processes, wal senders and wal receivers. It also gives information 85 | about replication slots. Except for the autovacuum workers count, most of this 86 | information is not related to a specific database, therefore their values will 87 | be zero when the data is filtered. 88 | 89 | Worker processes: 90 | 91 | =over 2 92 | 93 | =item - B: total worker count / maximum number of worker slots, parallel workers and logical replication workers are taken from this amount (filtered); 94 | 95 | =item - B: logical replication worker count / maximum number of logical replication workers (filtered); 96 | 97 | =item - B: parallel worker count for maintenance & queries / maximum number of parallel workers (filtered). 98 | 99 | =back 100 | 101 | Other processes & information: 102 | 103 | =over 2 104 | 105 | =item - B: number of autovacuum worker in action / maximum number of autovacuum workers (filtered); 106 | 107 | =item - B: number of wal senders / maximum number of wal senders processes (filtered); 108 | 109 | =item - B: number of wal receivers / maximum number of wal receiver processes (filtered); 110 | 111 | =item - B: number of replication slots / maximum number of replication slots (filtered). 112 | 113 | =back 114 | 115 | The last group displays B: 116 | 117 | =over 2 118 | 119 | =item - B: total / free / used and buff+cached memory with the related percentages; 120 | 121 | =item - B: total / free / used swap; 122 | 123 | =item - B: the number of IO per second, current Read and Write throughput (aggregated data gathered with the psutil library); 124 | 125 | =item - B: CPU load for the last 1, 5, 15 minutes; 126 | 127 | =back 128 | 129 | =head2 THE RUNNING QUERIES PANEL 130 | 131 | The running queries panel shows all running queries, transactions or backends 132 | (depending on the B setting) which have lasted for more than 133 | B seconds. It displays the following information: 134 | 135 | =over 2 136 | 137 | =item - B: process id of the backend which executes the query; 138 | 139 | =item - B: xmin horizon of the backend; 140 | 141 | =item - B: database specified in the connection string; 142 | 143 | =item - B: application name specified in the connection string; 144 | 145 | =item - B: user name specified in the connection string; 146 | 147 | =item - B: client address or "local" in case of linux socker connection; 148 | 149 | =item - B: percentage of CPU used by the backend as reported by the psutil library; 150 | 151 | =item - B: percentage of memory used by the backend as reported by the psutil library; 152 | 153 | =item - B: read thruput as reported by the psutil library; 154 | 155 | =item - B: write thruput as reported by the psutil library; 156 | 157 | =item - B