clickhouse-tool 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- clickhouse_tool-0.1.1/.gitignore +7 -0
- clickhouse_tool-0.1.1/LICENSE +21 -0
- clickhouse_tool-0.1.1/PKG-INFO +52 -0
- clickhouse_tool-0.1.1/README.md +24 -0
- clickhouse_tool-0.1.1/SKILL.md +234 -0
- clickhouse_tool-0.1.1/config.example.toml +40 -0
- clickhouse_tool-0.1.1/pyproject.toml +149 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/__about__.py +1 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/__init__.py +5 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/__main__.py +6 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/cli/__init__.py +0 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/cli/commands/__init__.py +0 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/cli/commands/config.py +21 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/cli/commands/facets.py +132 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/cli/commands/listing.py +45 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/cli/commands/logs.py +72 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/cli/commands/metrics.py +204 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/cli/commands/query.py +63 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/cli/commands/tables.py +25 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/cli/main.py +66 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/cli/runner.py +141 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/cli/wiring.py +95 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/core/__init__.py +0 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/core/client/__init__.py +0 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/core/client/base.py +34 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/core/client/factory.py +66 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/core/client/http.py +95 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/core/client/native.py +186 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/core/config.py +265 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/core/errors.py +126 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/core/exit_codes.py +25 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/core/humanize.py +58 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/core/logging.py +67 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/core/models.py +55 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/core/monitoring.py +32 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/core/ssm.py +77 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/core/timezone.py +41 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/formatters/__init__.py +47 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/formatters/base.py +54 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/formatters/json.py +43 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/formatters/ndjson.py +31 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/formatters/table.py +158 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/formatters/toon.py +70 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/query/__init__.py +0 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/query/builder.py +426 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/query/columns.py +34 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/query/intents.py +91 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/query/predicates.py +92 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/query/sort.py +44 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/query/tables.py +46 -0
- clickhouse_tool-0.1.1/src/clickhouse_tool/query/timerange.py +91 -0
- clickhouse_tool-0.1.1/tests/cli/__init__.py +0 -0
- clickhouse_tool-0.1.1/tests/cli/test_config.py +57 -0
- clickhouse_tool-0.1.1/tests/cli/test_facets.py +114 -0
- clickhouse_tool-0.1.1/tests/cli/test_listing.py +66 -0
- clickhouse_tool-0.1.1/tests/cli/test_logs.py +225 -0
- clickhouse_tool-0.1.1/tests/cli/test_main.py +96 -0
- clickhouse_tool-0.1.1/tests/cli/test_metrics.py +201 -0
- clickhouse_tool-0.1.1/tests/cli/test_query.py +80 -0
- clickhouse_tool-0.1.1/tests/cli/test_regression.py +249 -0
- clickhouse_tool-0.1.1/tests/cli/test_runner.py +282 -0
- clickhouse_tool-0.1.1/tests/cli/test_tables.py +59 -0
- clickhouse_tool-0.1.1/tests/cli/test_wiring.py +97 -0
- clickhouse_tool-0.1.1/tests/client/__init__.py +0 -0
- clickhouse_tool-0.1.1/tests/client/test_base.py +45 -0
- clickhouse_tool-0.1.1/tests/client/test_error_translation.py +87 -0
- clickhouse_tool-0.1.1/tests/client/test_factory.py +115 -0
- clickhouse_tool-0.1.1/tests/client/test_http.py +156 -0
- clickhouse_tool-0.1.1/tests/client/test_native.py +228 -0
- clickhouse_tool-0.1.1/tests/config/__init__.py +0 -0
- clickhouse_tool-0.1.1/tests/config/test_config.py +173 -0
- clickhouse_tool-0.1.1/tests/config/test_ssm.py +149 -0
- clickhouse_tool-0.1.1/tests/conftest.py +66 -0
- clickhouse_tool-0.1.1/tests/e2e/__init__.py +0 -0
- clickhouse_tool-0.1.1/tests/e2e/test_exit_codes.py +181 -0
- clickhouse_tool-0.1.1/tests/formatters/__init__.py +0 -0
- clickhouse_tool-0.1.1/tests/formatters/conftest.py +28 -0
- clickhouse_tool-0.1.1/tests/formatters/test_base.py +69 -0
- clickhouse_tool-0.1.1/tests/formatters/test_json.py +68 -0
- clickhouse_tool-0.1.1/tests/formatters/test_ndjson.py +48 -0
- clickhouse_tool-0.1.1/tests/formatters/test_registry.py +30 -0
- clickhouse_tool-0.1.1/tests/formatters/test_table.py +155 -0
- clickhouse_tool-0.1.1/tests/formatters/test_toon.py +84 -0
- clickhouse_tool-0.1.1/tests/formatters/test_toon_hostile.py +85 -0
- clickhouse_tool-0.1.1/tests/formatters/test_zero_result_conformance.py +39 -0
- clickhouse_tool-0.1.1/tests/integration/__init__.py +0 -0
- clickhouse_tool-0.1.1/tests/integration/conftest.py +93 -0
- clickhouse_tool-0.1.1/tests/integration/test_backends.py +55 -0
- clickhouse_tool-0.1.1/tests/query/__init__.py +0 -0
- clickhouse_tool-0.1.1/tests/query/test_builder.py +424 -0
- clickhouse_tool-0.1.1/tests/query/test_columns.py +37 -0
- clickhouse_tool-0.1.1/tests/query/test_intents.py +48 -0
- clickhouse_tool-0.1.1/tests/query/test_predicates.py +123 -0
- clickhouse_tool-0.1.1/tests/query/test_purity.py +49 -0
- clickhouse_tool-0.1.1/tests/query/test_sort.py +54 -0
- clickhouse_tool-0.1.1/tests/query/test_tables.py +37 -0
- clickhouse_tool-0.1.1/tests/query/test_timerange.py +76 -0
- clickhouse_tool-0.1.1/tests/test_errors.py +93 -0
- clickhouse_tool-0.1.1/tests/test_exit_codes.py +35 -0
- clickhouse_tool-0.1.1/tests/test_humanize.py +60 -0
- clickhouse_tool-0.1.1/tests/test_logging.py +65 -0
- clickhouse_tool-0.1.1/tests/test_models.py +51 -0
- clickhouse_tool-0.1.1/tests/test_monitoring.py +41 -0
- clickhouse_tool-0.1.1/tests/test_package.py +29 -0
- clickhouse_tool-0.1.1/tests/test_timezone.py +49 -0
- clickhouse_tool-0.1.1/uv.lock +1340 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vlad Korolev
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: clickhouse-tool
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: ClickHouse query and administration CLI tool for the OTel logs/metrics pilot
|
|
5
|
+
Author-email: Vlad Korolev <vlad@v-lad.org>
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Requires-Dist: boto3>=1.34.0
|
|
9
|
+
Requires-Dist: pydantic>=2.5.0
|
|
10
|
+
Requires-Dist: rich>=13.0.0
|
|
11
|
+
Requires-Dist: sentry-sdk>=1.40.0
|
|
12
|
+
Requires-Dist: structlog>=24.0
|
|
13
|
+
Requires-Dist: typer>=0.12.0
|
|
14
|
+
Provides-Extra: all
|
|
15
|
+
Requires-Dist: clickhouse-connect>=0.7.0; extra == 'all'
|
|
16
|
+
Requires-Dist: clickhouse-driver>=0.2.9; extra == 'all'
|
|
17
|
+
Provides-Extra: dev
|
|
18
|
+
Requires-Dist: mypy>=1.13; extra == 'dev'
|
|
19
|
+
Requires-Dist: pre-commit>=4.0; extra == 'dev'
|
|
20
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
21
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
22
|
+
Requires-Dist: ruff>=0.8; extra == 'dev'
|
|
23
|
+
Provides-Extra: http
|
|
24
|
+
Requires-Dist: clickhouse-connect>=0.7.0; extra == 'http'
|
|
25
|
+
Provides-Extra: native
|
|
26
|
+
Requires-Dist: clickhouse-driver>=0.2.9; extra == 'native'
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# clickhouse-tool
|
|
30
|
+
|
|
31
|
+
A command-line client for querying and inspecting ClickHouse, over both
|
|
32
|
+
the native protocol (port 9000) and HTTP (port 8123).
|
|
33
|
+
|
|
34
|
+
`clickhouse-tool` provides porcelain commands (`logs`, `hosts`, `services`)
|
|
35
|
+
for browsing OTel-shaped log data, plus low-level verbs (`query`, `list`,
|
|
36
|
+
`lift`, `config`) for direct inspection of any table. Output renders as a
|
|
37
|
+
table, JSON, NDJSON, or the compact TOON format, with pinned typed exit
|
|
38
|
+
codes for scripting.
|
|
39
|
+
|
|
40
|
+
## Install
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
pip install clickhouse-tool[native] # native protocol (port 9000)
|
|
44
|
+
pip install clickhouse-tool[http] # HTTP protocol (port 8123)
|
|
45
|
+
pip install clickhouse-tool[all] # both
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Configuration
|
|
49
|
+
|
|
50
|
+
Copy `config.example.toml` to `./clickhouse-tool.toml` or
|
|
51
|
+
`~/.config/clickhouse-tool/config.toml` and fill in a profile for your
|
|
52
|
+
cluster. See that file for the full set of options.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# clickhouse-tool
|
|
2
|
+
|
|
3
|
+
A command-line client for querying and inspecting ClickHouse, over both
|
|
4
|
+
the native protocol (port 9000) and HTTP (port 8123).
|
|
5
|
+
|
|
6
|
+
`clickhouse-tool` provides porcelain commands (`logs`, `hosts`, `services`)
|
|
7
|
+
for browsing OTel-shaped log data, plus low-level verbs (`query`, `list`,
|
|
8
|
+
`lift`, `config`) for direct inspection of any table. Output renders as a
|
|
9
|
+
table, JSON, NDJSON, or the compact TOON format, with pinned typed exit
|
|
10
|
+
codes for scripting.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
pip install clickhouse-tool[native] # native protocol (port 9000)
|
|
16
|
+
pip install clickhouse-tool[http] # HTTP protocol (port 8123)
|
|
17
|
+
pip install clickhouse-tool[all] # both
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Configuration
|
|
21
|
+
|
|
22
|
+
Copy `config.example.toml` to `./clickhouse-tool.toml` or
|
|
23
|
+
`~/.config/clickhouse-tool/config.toml` and fill in a profile for your
|
|
24
|
+
cluster. See that file for the full set of options.
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# clickhouse-tool
|
|
2
|
+
|
|
3
|
+
CLI to query and inspect ClickHouse, over both the native protocol
|
|
4
|
+
(port 9000) and HTTP (port 8123). Invoked as `clickhouse-tool`.
|
|
5
|
+
|
|
6
|
+
## When to Use / When NOT to Use
|
|
7
|
+
|
|
8
|
+
| Use clickhouse-tool for | Do NOT use for |
|
|
9
|
+
|--------------------------|-----------------|
|
|
10
|
+
| Browsing OTel-shaped logs with a `logs`/`hosts`/`services` porcelain | Writes or DDL (the tool is read-only by design) |
|
|
11
|
+
| Counting hosts/services over logs or metrics activity | Detailed metric values/time series (`hosts`/`services` only count activity, not metric values) |
|
|
12
|
+
| Inspecting any allow-listed table's rows/columns | Cluster administration (node/shard management, replication control) |
|
|
13
|
+
| Running direct SQL against the cluster | |
|
|
14
|
+
| Scripting against pinned exit codes | |
|
|
15
|
+
|
|
16
|
+
## Quick Reference
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
clickhouse-tool logs --service payments -l error -t 1h # recent error logs for a service
|
|
20
|
+
clickhouse-tool logs --follow --host k8s-3 # tail new rows continuously
|
|
21
|
+
clickhouse-tool hosts -t 15m # host counts, last 15 minutes
|
|
22
|
+
clickhouse-tool services --unbounded # every service, no cap
|
|
23
|
+
clickhouse-tool metrics names -n 10 # most active metric names
|
|
24
|
+
clickhouse-tool metrics status k8s.node.memory -t 1h # min/max/avg/stddev per metric
|
|
25
|
+
clickhouse-tool metrics histogram system.cpu.utilization -t 1h # time-bucketed stats
|
|
26
|
+
clickhouse-tool tables # every table list/lift/query may target
|
|
27
|
+
clickhouse-tool list otel.otel_logs -n 5 # peek at raw rows
|
|
28
|
+
clickhouse-tool lift otel.otel_logs # describe columns
|
|
29
|
+
clickhouse-tool query "SELECT count() FROM otel.otel_logs" # raw SQL
|
|
30
|
+
clickhouse-tool config # show resolved profile, no AWS needed
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Commands
|
|
34
|
+
|
|
35
|
+
### `logs` — the baseline verb
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
clickhouse-tool logs [--host/-h HOST] [--service SERVICE] [--namespace NS]
|
|
39
|
+
[--container C] [--level/-l LEVEL] [-t/--time-range 15m|2h|1d]
|
|
40
|
+
[--search/-s TERM] [--sort SORT] [--full] [--raw]
|
|
41
|
+
[--count] [--follow] [--utc] [-n/--limit N]
|
|
42
|
+
[-f/--format table|json|ndjson|toon]
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
- `--count` returns a single row count instead of rows.
|
|
46
|
+
- `--full`/`--raw` select every column and switch to nested output — the only
|
|
47
|
+
verb that may stream (via the `ndjson` formatter).
|
|
48
|
+
- `--follow` polls continuously for new rows, oldest first, until Ctrl-C.
|
|
49
|
+
`-t` seeds the starting point (how far back the first batch reaches);
|
|
50
|
+
after that, only rows newer than the last one seen are printed. Not
|
|
51
|
+
compatible with `--count` or `--sort` (follow mode always orders
|
|
52
|
+
ascending by time).
|
|
53
|
+
- The `ts` column renders in local time by default; `--utc` shows UTC
|
|
54
|
+
instead. The profile's `default_timezone` sets the non-`--utc` default
|
|
55
|
+
(`local`, `UTC`, or any IANA zone name — `local` resolves to the
|
|
56
|
+
machine's own zone). `--full`/`--raw` show the raw server value
|
|
57
|
+
unconverted, since they select `*` directly, not the `ts` alias.
|
|
58
|
+
- Time window defaults to the profile's `default_window` (`2h` in the
|
|
59
|
+
example config) when `-t` is omitted.
|
|
60
|
+
|
|
61
|
+
### `hosts` / `services` — grouped counts
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
clickhouse-tool hosts [--namespace NS] [--container C] [--level/-l LEVEL]
|
|
65
|
+
[-t/--time-range WINDOW] [--search/-s TERM]
|
|
66
|
+
[--stream logs|metrics] [-n/--limit N] [--unbounded]
|
|
67
|
+
[-f/--format ...]
|
|
68
|
+
clickhouse-tool services [same flags]
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
- Grouped by host or service, ordered by count descending, capped at 200
|
|
72
|
+
rows unless `--unbounded`. Each row also shows `last_seen`, rendered as a
|
|
73
|
+
relative time (`2h 20m ago`) -- a second unit only above a 10 threshold,
|
|
74
|
+
seconds is the finest granularity shown.
|
|
75
|
+
- `--stream metrics` (default) counts metric datapoints -- useful since not
|
|
76
|
+
every host/service that emits metrics also emits logs. `--stream logs`
|
|
77
|
+
counts log rows instead. Metrics mode only supports `-t`/`--time-range`;
|
|
78
|
+
`--namespace`, `--container`, `--level`, `--search` all raise `ConfigError`
|
|
79
|
+
(exit 3) on that stream, since metrics carry none of those attributes.
|
|
80
|
+
|
|
81
|
+
### `metrics` — metric names, stats, values, and histograms
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
clickhouse-tool metrics names [-t/--time-range WINDOW] [-n/--limit N] [--unbounded] [-f/--format ...]
|
|
85
|
+
clickhouse-tool metrics status <name> [--host H] [--service S] [-t/--time-range WINDOW]
|
|
86
|
+
[--split-by host,service] [-f/--format ...]
|
|
87
|
+
clickhouse-tool metrics values <name> [--host H] [--service S] [-t/--time-range WINDOW]
|
|
88
|
+
[--follow] [-n/--limit N] [-f/--format ...]
|
|
89
|
+
clickhouse-tool metrics histogram <name> [--host H] [--service S] [-t/--time-range WINDOW]
|
|
90
|
+
[--bucket WIDTH] [--split-by host,service] [-f/--format ...]
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
- `<name>` matches an exact metric name (`k8s.node.filesystem.capacity`) or a
|
|
94
|
+
dotted stem matching every child metric (`k8s.node.memory`).
|
|
95
|
+
- `names` groups by metric name over the metrics stream, ordered by
|
|
96
|
+
datapoint count descending, capped at 200 unless `--unbounded`. Same
|
|
97
|
+
`last_seen` column as `hosts`/`services`.
|
|
98
|
+
- `status` reports `min`/`max`/`avg`/`stddev` per matched metric name.
|
|
99
|
+
`--split-by host,service` breaks the summary out per host and/or
|
|
100
|
+
service instead of merging them.
|
|
101
|
+
- `values` returns raw datapoints oldest-first, capped at 1000 unless
|
|
102
|
+
`-n` overrides. `--follow` polls continuously starting from the most
|
|
103
|
+
recent matching datapoint (not `-n`-compatible), same semantics as
|
|
104
|
+
`logs --follow`.
|
|
105
|
+
- `histogram` buckets datapoints by time, reporting `min`/`max`/`avg`/`count`
|
|
106
|
+
per bucket per matched metric name. `--bucket` sets the width explicitly
|
|
107
|
+
(`5m`, `1h`, ...); omitted, it's sized automatically from the time window
|
|
108
|
+
to land around 36 buckets, snapped to a round duration (seconds through
|
|
109
|
+
weeks). `--split-by host,service` breaks each metric's buckets out per
|
|
110
|
+
host and/or service instead of merging them.
|
|
111
|
+
|
|
112
|
+
### `list` / `lift` — low-level table inspection
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
clickhouse-tool list <table> [-n/--limit N] [-f/--format ...] # raw rows
|
|
116
|
+
clickhouse-tool lift <table> [-f/--format ...] # column metadata
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
- `<table>` must be allow-listed: any `otel.<identifier>`, or one of
|
|
120
|
+
`system.parts`, `.errors`, `.tables`, `.columns`, `.metrics`,
|
|
121
|
+
`.asynchronous_metrics`, `.query_log`.
|
|
122
|
+
- `lift` defaults to `json` output (nested-friendly), not the profile's
|
|
123
|
+
`default_format`.
|
|
124
|
+
|
|
125
|
+
### `query` — raw SQL
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
clickhouse-tool query "SELECT ... " [--param name=value ...] [-f/--format ...]
|
|
129
|
+
echo "SELECT 1" | clickhouse-tool query
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
- SQL comes from the positional argument, or from stdin if no argument is
|
|
133
|
+
given and stdin is not a TTY — never both at once.
|
|
134
|
+
- `--param` is repeatable, binds named parameters as strings.
|
|
135
|
+
- Bypasses the query builder entirely: no table allow-list, no column
|
|
136
|
+
validation. Read-only safety comes **solely** from the server-side
|
|
137
|
+
`readonly=2` setting, not from anything this tool parses. Server errors
|
|
138
|
+
(including malformed SQL) echo back verbatim — the caller wrote the query.
|
|
139
|
+
|
|
140
|
+
### `config` — show the resolved profile
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
clickhouse-tool config [--profile NAME]
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
- Never touches AWS SSM and never opens a connection — works with zero AWS
|
|
147
|
+
credentials present. Secrets print as unresolved references
|
|
148
|
+
(`(unresolved, ssm:<path>)`), never as values.
|
|
149
|
+
|
|
150
|
+
### `tables` — list what `list`/`lift`/`query` may target
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
clickhouse-tool tables [--profile NAME] [-f/--format ...]
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
- Combines the profile's `otel.*` tables (queried live from the cluster)
|
|
157
|
+
with the seven fixed `system.*` tables the allow-list also accepts.
|
|
158
|
+
Unlike `config`, this connects to the cluster.
|
|
159
|
+
|
|
160
|
+
## Profiles and Connection
|
|
161
|
+
|
|
162
|
+
- `--profile NAME` on any command selects a named profile from config.
|
|
163
|
+
- Config precedence, highest first: CLI flag > `CLICKHOUSE_TOOL_*` env var >
|
|
164
|
+
selected profile > project-local `./clickhouse-tool.toml` > user
|
|
165
|
+
`~/.config/clickhouse-tool/config.toml` > built-in defaults.
|
|
166
|
+
- Copy `config.example.toml` to get started; it ships a `pilot` (native,
|
|
167
|
+
port 9000) and `pilot-http` (HTTP, port 8123) profile pointed at the same
|
|
168
|
+
cluster, with credentials resolved lazily from AWS SSM (`user`/`password`
|
|
169
|
+
as `{ ssm = "<path>" }`).
|
|
170
|
+
- Secrets are resolved lazily — only verbs that actually connect touch SSM.
|
|
171
|
+
`config` never does.
|
|
172
|
+
|
|
173
|
+
## Output Formats
|
|
174
|
+
|
|
175
|
+
| Flag | Use case |
|
|
176
|
+
|------|----------|
|
|
177
|
+
| `-f json` | Agent processing, or nested output (`lift`, `--full`, `--raw`) — buffered array, Map columns kept nested |
|
|
178
|
+
| `-f ndjson` | Streaming large result sets row-by-row — never buffers the full set |
|
|
179
|
+
| `-f table` | Presenting results to a human in a terminal — rich table, null renders `-` |
|
|
180
|
+
| `-f toon` | Default format — compact `name[N]{fields}` dialect, cheapest to read/grep |
|
|
181
|
+
|
|
182
|
+
Format defaults to the profile's `default_format` (`toon` in the example
|
|
183
|
+
config) when `-f` is omitted, except `lift`, which defaults to `json`.
|
|
184
|
+
|
|
185
|
+
`-f table` humanizes numbers and timestamps; every other format renders raw
|
|
186
|
+
values, unaffected by any of the flags below:
|
|
187
|
+
- Numbers get a k/m/g suffix once the scaled value would read 10 or
|
|
188
|
+
higher (`1024` stays `1024`, `10000` becomes `10k`), rounded to 2
|
|
189
|
+
decimal places by default -- override with `--decimals N` (`metrics
|
|
190
|
+
status`/`values`/`histogram`).
|
|
191
|
+
- Timestamps drop their seconds component once the queried time window
|
|
192
|
+
exceeds 3 hours, showing whole seconds below that. `--ts-decimals N`
|
|
193
|
+
(`metrics values`/`histogram`) forces second-level display with `N`
|
|
194
|
+
fractional digits, overriding the window-based default.
|
|
195
|
+
|
|
196
|
+
## Error Handling
|
|
197
|
+
|
|
198
|
+
| Exit code | Meaning | Agent action |
|
|
199
|
+
|-----------|---------|---------------|
|
|
200
|
+
| 0 | Success | Proceed |
|
|
201
|
+
| 1 | General/unexpected error | Report — no documented outcome maps here |
|
|
202
|
+
| 2 | Usage error | Fix command syntax |
|
|
203
|
+
| 3 | Config error (bad profile, unknown column/table/sort, malformed `--param`, missing driver, bad `--stream` value) | Check flags/profile, fix and retry |
|
|
204
|
+
| 4 | Connection failed | Cluster unreachable — check host/port/transport |
|
|
205
|
+
| 5 | Auth failed | Check credentials / SSM refs |
|
|
206
|
+
| 6 | Query timeout | Narrow the query or raise `query_timeout` in the profile |
|
|
207
|
+
| 7 | Result too large | Narrow the time window or add a `--limit` |
|
|
208
|
+
| 8 | Server resource error | Cluster is under memory/resource pressure — retry later |
|
|
209
|
+
| 9 | Permission denied | Check the ClickHouse user's grants |
|
|
210
|
+
| 10 | Schema error | Table/column doesn't exist as named — check `lift` |
|
|
211
|
+
| 11 | Unsupported filter (e.g. `--container` on a key that doesn't exist) | The container-preflight check failed — verify the filter value |
|
|
212
|
+
| 12 | Server error (unmapped ClickHouse code) | Server returned an error code with no local mapping — report the message |
|
|
213
|
+
|
|
214
|
+
Every typed error prints its message to stderr; stdout is reserved for
|
|
215
|
+
data only. `BrokenPipeError` (e.g. piping into `head`) exits 0 quietly,
|
|
216
|
+
no traceback.
|
|
217
|
+
|
|
218
|
+
## Global Options
|
|
219
|
+
|
|
220
|
+
| Flag | Purpose |
|
|
221
|
+
|------|---------|
|
|
222
|
+
| `--profile NAME` | Select connection profile |
|
|
223
|
+
| `-f/--format table\|json\|ndjson\|toon` | Output format |
|
|
224
|
+
| `-v/--verbose` | Enable debug logging |
|
|
225
|
+
| `--help` | Show help (note: `-h` is reserved for `--host` on `logs`, not help) |
|
|
226
|
+
|
|
227
|
+
## Known Limitations
|
|
228
|
+
|
|
229
|
+
- No cluster administration commands (node/shard/replication management)
|
|
230
|
+
— use ClickHouse's own tooling for that.
|
|
231
|
+
- `hosts --stream metrics` / `services --stream metrics` only enumerate
|
|
232
|
+
activity against `otel_metrics_gauge`; the other metrics tables
|
|
233
|
+
(`otel_metrics_sum`, `otel_metrics_histogram`, etc. — see `tables`) have
|
|
234
|
+
no porcelain support yet — use `list`/`query` directly against them.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Example clickhouse-tool configuration.
|
|
2
|
+
#
|
|
3
|
+
# Copy to ./clickhouse-tool.toml (project-local) or
|
|
4
|
+
# ~/.config/clickhouse-tool/config.toml (user-level). Project-local wins
|
|
5
|
+
# on any field present in both.
|
|
6
|
+
|
|
7
|
+
default_profile = "pilot" # never "default"
|
|
8
|
+
default_format = "toon"
|
|
9
|
+
default_window = "2h"
|
|
10
|
+
default_timezone = "local" # "local", "UTC", or any IANA zone name
|
|
11
|
+
|
|
12
|
+
[profiles.pilot]
|
|
13
|
+
host = "your-clickhouse-host.example.com"
|
|
14
|
+
port = 9000
|
|
15
|
+
transport = "native" # "native" | "http", both implemented
|
|
16
|
+
database = "otel"
|
|
17
|
+
secure = false
|
|
18
|
+
logs_table = "otel_logs"
|
|
19
|
+
connect_timeout = 10
|
|
20
|
+
query_timeout = 60
|
|
21
|
+
|
|
22
|
+
[profiles.pilot.user]
|
|
23
|
+
ssm = "/clickhouse/v1/pilot/tool/username"
|
|
24
|
+
[profiles.pilot.password]
|
|
25
|
+
ssm = "/clickhouse/v1/pilot/tool/password"
|
|
26
|
+
|
|
27
|
+
[profiles.pilot-http] # HTTP-transport variant of the same pilot
|
|
28
|
+
host = "your-clickhouse-host.example.com"
|
|
29
|
+
port = 8123
|
|
30
|
+
transport = "http"
|
|
31
|
+
database = "otel"
|
|
32
|
+
secure = false
|
|
33
|
+
logs_table = "otel_logs"
|
|
34
|
+
connect_timeout = 10
|
|
35
|
+
query_timeout = 60
|
|
36
|
+
|
|
37
|
+
[profiles.pilot-http.user]
|
|
38
|
+
ssm = "/clickhouse/v1/pilot/tool/username"
|
|
39
|
+
[profiles.pilot-http.password]
|
|
40
|
+
ssm = "/clickhouse/v1/pilot/tool/password"
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "clickhouse-tool"
|
|
7
|
+
version = "0.1.1"
|
|
8
|
+
description = "ClickHouse query and administration CLI tool for the OTel logs/metrics pilot"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
authors = [
|
|
11
|
+
{name = "Vlad Korolev", email = "vlad@v-lad.org"}
|
|
12
|
+
]
|
|
13
|
+
requires-python = ">=3.11"
|
|
14
|
+
dependencies = [
|
|
15
|
+
"typer>=0.12.0",
|
|
16
|
+
"pydantic>=2.5.0",
|
|
17
|
+
"rich>=13.0.0",
|
|
18
|
+
"structlog>=24.0",
|
|
19
|
+
"sentry-sdk>=1.40.0",
|
|
20
|
+
"boto3>=1.34.0"
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[project.optional-dependencies]
|
|
24
|
+
native = [
|
|
25
|
+
"clickhouse-driver>=0.2.9"
|
|
26
|
+
]
|
|
27
|
+
http = [
|
|
28
|
+
"clickhouse-connect>=0.7.0"
|
|
29
|
+
]
|
|
30
|
+
all = [
|
|
31
|
+
"clickhouse-tool[native]",
|
|
32
|
+
"clickhouse-tool[http]"
|
|
33
|
+
]
|
|
34
|
+
dev = [
|
|
35
|
+
"pytest>=8.0",
|
|
36
|
+
"pytest-cov>=4.0",
|
|
37
|
+
"ruff>=0.8",
|
|
38
|
+
"mypy>=1.13",
|
|
39
|
+
"pre-commit>=4.0"
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[project.scripts]
|
|
43
|
+
clickhouse-tool = "clickhouse_tool.cli.main:run"
|
|
44
|
+
|
|
45
|
+
[tool.coverage.report]
|
|
46
|
+
exclude_lines = [
|
|
47
|
+
"pragma: no cover",
|
|
48
|
+
"def __repr__",
|
|
49
|
+
"if self.debug:",
|
|
50
|
+
"if settings.DEBUG",
|
|
51
|
+
"raise AssertionError",
|
|
52
|
+
"raise NotImplementedError",
|
|
53
|
+
"if 0:",
|
|
54
|
+
"if __name__ == .__main__.:",
|
|
55
|
+
"class .*\\bProtocol\\):",
|
|
56
|
+
"@(abc\\.)?abstractmethod"
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
[tool.coverage.run]
|
|
60
|
+
source = ["src"]
|
|
61
|
+
omit = [
|
|
62
|
+
"*/tests/*",
|
|
63
|
+
"*/test_*.py",
|
|
64
|
+
"*/__about__.py"
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
[tool.hatch.build.targets.wheel]
|
|
68
|
+
packages = ["src/clickhouse_tool"]
|
|
69
|
+
|
|
70
|
+
[tool.mypy]
|
|
71
|
+
python_version = "3.11"
|
|
72
|
+
plugins = ["pydantic.mypy"]
|
|
73
|
+
strict = true
|
|
74
|
+
warn_return_any = true
|
|
75
|
+
warn_unused_configs = true
|
|
76
|
+
disallow_untyped_defs = true
|
|
77
|
+
disallow_any_generics = true
|
|
78
|
+
no_implicit_optional = true
|
|
79
|
+
warn_redundant_casts = true
|
|
80
|
+
warn_unused_ignores = true
|
|
81
|
+
warn_unreachable = true
|
|
82
|
+
strict_equality = true
|
|
83
|
+
exclude = ["tests/"]
|
|
84
|
+
|
|
85
|
+
[[tool.mypy.overrides]]
|
|
86
|
+
module = "tests.*"
|
|
87
|
+
disallow_untyped_defs = false
|
|
88
|
+
|
|
89
|
+
[[tool.mypy.overrides]]
|
|
90
|
+
module = ["clickhouse_driver.*", "clickhouse_connect.*"]
|
|
91
|
+
ignore_missing_imports = true
|
|
92
|
+
|
|
93
|
+
[tool.pytest.ini_options]
|
|
94
|
+
minversion = "7.0"
|
|
95
|
+
testpaths = ["tests"]
|
|
96
|
+
pythonpath = ["src"]
|
|
97
|
+
addopts = [
|
|
98
|
+
"--strict-markers",
|
|
99
|
+
"--strict-config",
|
|
100
|
+
"--cov=clickhouse_tool",
|
|
101
|
+
"--cov-report=term-missing",
|
|
102
|
+
"--cov-report=html:htmlcov",
|
|
103
|
+
"--cov-report=xml",
|
|
104
|
+
"--cov-fail-under=80"
|
|
105
|
+
]
|
|
106
|
+
markers = [
|
|
107
|
+
"unit: marks tests as unit tests (fast, no external dependencies)",
|
|
108
|
+
"integration: marks tests as integration tests (requires the real ClickHouse pilot)",
|
|
109
|
+
"slow: marks tests as slow (deselect with '-m \"not slow\"')"
|
|
110
|
+
]
|
|
111
|
+
filterwarnings = [
|
|
112
|
+
"error",
|
|
113
|
+
"ignore::UserWarning",
|
|
114
|
+
"ignore::DeprecationWarning"
|
|
115
|
+
]
|
|
116
|
+
|
|
117
|
+
[tool.ruff]
|
|
118
|
+
target-version = "py311"
|
|
119
|
+
line-length = 100
|
|
120
|
+
|
|
121
|
+
[tool.ruff.lint]
|
|
122
|
+
select = [
|
|
123
|
+
"E",
|
|
124
|
+
"F",
|
|
125
|
+
"I",
|
|
126
|
+
"N",
|
|
127
|
+
"W",
|
|
128
|
+
"UP",
|
|
129
|
+
"B",
|
|
130
|
+
"C4",
|
|
131
|
+
"SIM",
|
|
132
|
+
"PTH",
|
|
133
|
+
"RUF",
|
|
134
|
+
"S"
|
|
135
|
+
]
|
|
136
|
+
ignore = [
|
|
137
|
+
"E501",
|
|
138
|
+
"B008",
|
|
139
|
+
"N818" # exception names are the plan's pinned contract (AuthFailed, QueryTimeout, ...)
|
|
140
|
+
]
|
|
141
|
+
fixable = ["ALL"]
|
|
142
|
+
unfixable = []
|
|
143
|
+
|
|
144
|
+
[tool.ruff.lint.isort]
|
|
145
|
+
known-first-party = ["clickhouse_tool"]
|
|
146
|
+
|
|
147
|
+
[tool.ruff.lint.per-file-ignores]
|
|
148
|
+
"__init__.py" = ["F401"]
|
|
149
|
+
"tests/**/*.py" = ["S101"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""The skip-secrets verb: shows the resolved profile with provenance.
|
|
2
|
+
|
|
3
|
+
Never materialises secrets and never opens a connection, so it works
|
|
4
|
+
with no AWS credentials present at all.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
|
|
11
|
+
import typer
|
|
12
|
+
|
|
13
|
+
from clickhouse_tool.cli import wiring
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def config(
|
|
17
|
+
profile: str | None = typer.Option(None, "--profile", help="Named connection profile"),
|
|
18
|
+
) -> None:
|
|
19
|
+
"""Show the resolved configuration, with secrets redacted."""
|
|
20
|
+
resolved = wiring.load_profile(profile)
|
|
21
|
+
print(json.dumps(resolved.model_dump(), indent=2, default=str))
|