Skip to content

Commit

Permalink
cratedb-wtf: Add cratedb-wtf diagnostics program
Browse files Browse the repository at this point in the history
  • Loading branch information
amotl committed Dec 4, 2023
1 parent 7f3a493 commit 0e3ca31
Show file tree
Hide file tree
Showing 20 changed files with 1,065 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
- MongoDB: Improve UX by using `ctk load table mongodb://...`
- load table: Refactor to use more OO
- Add `examples/cloud_import.py`
- Add `cratedb-wtf` diagnostics program


## 2023/11/06 v0.0.2
Expand Down
2 changes: 2 additions & 0 deletions cratedb_toolkit/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from .io.cli import cli as io_cli
from .job.cli import cli_list_jobs
from .shell.cli import cli as shell_cli
from .wtf.cli import cli as wtf_cli


@click.group(cls=ClickAliasedGroup) # type: ignore[arg-type]
Expand All @@ -21,4 +22,5 @@ def cli(ctx: click.Context, verbose: bool, debug: bool):
cli.add_command(cloud_cli, name="cluster")
cli.add_command(io_cli, name="load")
cli.add_command(shell_cli, name="shell")
cli.add_command(wtf_cli, name="wtf")
cli.add_command(cli_list_jobs)
8 changes: 5 additions & 3 deletions cratedb_toolkit/util/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,15 @@ def boot_with_dburi():
return dburi


def make_command(cli, name, helpfun=None, aliases=None):
def make_command(cli, name, help=None, aliases=None): # noqa: A002
"""
Convenience shortcut for creating a subcommand.
"""
kwargs = {}
if helpfun:
kwargs["help"] = docstring_format_verbatim(helpfun.__doc__)
if isinstance(help, str):
kwargs["help"] = help
elif callable(help):
kwargs["help"] = docstring_format_verbatim(help.__doc__)
return cli.command(
name,
context_settings={"max_content_width": 120},
Expand Down
15 changes: 14 additions & 1 deletion cratedb_toolkit/util/data.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import datetime as dt
import json
import sys
import typing as t
Expand All @@ -7,7 +8,7 @@ def jd(data: t.Any):
"""
Pretty-print JSON with indentation.
"""
print(json.dumps(data, indent=2), file=sys.stdout) # noqa: T201
print(json.dumps(data, indent=2, cls=JSONEncoderPlus), file=sys.stdout) # noqa: T201


def str_contains(haystack, *needles):
Expand All @@ -16,3 +17,15 @@ def str_contains(haystack, *needles):
"""
haystack = str(haystack)
return any(needle in haystack for needle in needles)


class JSONEncoderPlus(json.JSONEncoder):
"""
https://stackoverflow.com/a/27058505
"""

def default(self, o):
if isinstance(o, dt.datetime):
return o.isoformat()

return json.JSONEncoder.default(self, o)

Check warning on line 31 in cratedb_toolkit/util/data.py

View check run for this annotation

Codecov / codecov/patch

cratedb_toolkit/util/data.py#L31

Added line #L31 was not covered by tests
56 changes: 56 additions & 0 deletions cratedb_toolkit/util/platform.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import io
import json
from contextlib import redirect_stdout


class PlatformInfo:
@staticmethod
def application():
import platform

from cratedb_toolkit import __appname__, __version__

data = {}

data["platform"] = platform.platform()
data["version"] = __version__
data["name"] = __appname__
return data

@staticmethod
def libraries():
data = {}

# SQLAlchemy
from importlib.metadata import entry_points

try:
import sqlalchemy.dialects.plugins
import sqlalchemy.dialects.registry

Check warning on line 29 in cratedb_toolkit/util/platform.py

View check run for this annotation

Codecov / codecov/patch

cratedb_toolkit/util/platform.py#L29

Added line #L29 was not covered by tests

data["sqlalchemy"] = {

Check warning on line 31 in cratedb_toolkit/util/platform.py

View check run for this annotation

Codecov / codecov/patch

cratedb_toolkit/util/platform.py#L31

Added line #L31 was not covered by tests
"dialects_builtin": list(sqlalchemy.dialects.registry.impls.keys()),
"dialects_3rdparty": [dialect.name for dialect in entry_points(group="sqlalchemy.dialects")], # type: ignore[attr-defined,call-arg]
"plugins": list(sqlalchemy.dialects.plugins.impls.keys()),
}
except Exception: # noqa: S110
pass

# pandas
try:
import pandas

buffer = io.StringIO()
with redirect_stdout(buffer):
pandas.show_versions(as_json=True)
buffer.seek(0)
data["pandas"] = json.load(buffer)
except Exception: # noqa: S110
pass

Check warning on line 49 in cratedb_toolkit/util/platform.py

View check run for this annotation

Codecov / codecov/patch

cratedb_toolkit/util/platform.py#L48-L49

Added lines #L48 - L49 were not covered by tests

# fsspec
import fsspec

data["fsspec"] = {"protocols": fsspec.available_protocols(), "compressions": fsspec.available_compressions()}

return data
24 changes: 24 additions & 0 deletions cratedb_toolkit/util/service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright (c) 2021-2023, Crate.io Inc.
# Distributed under the terms of the AGPLv3 license, see LICENSE.
import logging

Check warning on line 3 in cratedb_toolkit/util/service.py

View check run for this annotation

Codecov / codecov/patch

cratedb_toolkit/util/service.py#L3

Added line #L3 was not covered by tests

import typting as t

Check warning on line 5 in cratedb_toolkit/util/service.py

View check run for this annotation

Codecov / codecov/patch

cratedb_toolkit/util/service.py#L5

Added line #L5 was not covered by tests

from cratedb_toolkit.util.common import setup_logging

Check warning on line 7 in cratedb_toolkit/util/service.py

View check run for this annotation

Codecov / codecov/patch

cratedb_toolkit/util/service.py#L7

Added line #L7 was not covered by tests

logger = logging.getLogger(__name__)

Check warning on line 9 in cratedb_toolkit/util/service.py

View check run for this annotation

Codecov / codecov/patch

cratedb_toolkit/util/service.py#L9

Added line #L9 was not covered by tests


def start_service(app: str, listen_address: t.Union[str, None] = None, reload: bool = False): # pragma: no cover
setup_logging()
from uvicorn import run

if listen_address is None:
listen_address = "127.0.0.1:4242"

host, port = listen_address.split(":")
port_int = int(port)

logger.info(f"Starting HTTP web service on http://{listen_address}")

run(app=app, host=host, port=port_int, reload=reload)
47 changes: 47 additions & 0 deletions cratedb_toolkit/wtf/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# cratedb-wtf

A diagnostics utility in the spirit of [git-wtf], [grafana-wtf], and [pip.wtf].
It is still a work-in-progress, but it is usable already.


## Synopsis

Define CrateDB database cluster address.
```shell
export CRATEDB_SQLALCHEMY_URL=crate://localhost/
```

Display system and database cluster information.
```shell
cratedb-wtf info
```

Display database cluster log messages.
```shell
cratedb-wtf logs
```

Statistics.
```shell
cratedb-wtf statistics quick
cratedb-wtf statistics collect
cratedb-wtf statistics view
```


## HTTP API

Expose collected status information.
```shell
cratedb-wtf --debug serve --reload
```
Consume collected status information via HTTP.
```shell
http http://127.0.0.1:4242/info/all
```



[git-wtf]: http://thrawn01.org/posts/2014/03/03/git-wtf/
[grafana-wtf]: https://github.com/panodata/grafana-wtf
[pip.wtf]: https://github.com/sabslikesobs/pip.wtf
Empty file added cratedb_toolkit/wtf/__init__.py
Empty file.
35 changes: 35 additions & 0 deletions cratedb_toolkit/wtf/backlog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# cratedb-wtf backlog

## Iteration +1
- Complete collected queries and code snippets
- Harvest queries from Admin UI, crash, crate-admin-alt
- Harvest queries from experts
- https://tools.cr8.net/grafana/d/RkpNJx84z/cratedb-jobs-log?orgId=1&refresh=5m&var-datasource=crate-production
- https://tools.cr8.net/grafana/d/RkpNJx84z/cratedb-jobs-log?orgId=1&refresh=5m&var-datasource=crate-production&viewPanel=44
- Add `description` field to each `InfoElement`

## Iteration +2
- Make `cratedb-wtf logs` also optionally consider `sys.` tables.
- cratedb-wtf explore table|shard|partition|node
- High-level analysis, evaluating a set of threshold rules
- Network diagnostics?
- Expose collected data via Glances-like UI

## Notes
```
ctk cluster info
ctk cluster health
ctk cluster logs --slow-queries
```

Acknowledgements: @baur, @hammerhea, @walbeh.


## Done
- Make it work
- Proper marshalling of timestamp values (ISO 8601)
- Expose collected data via HTTP API
```
cratedb-wtf serve
```
- Provide `scrub` option also via HTTP
Loading

0 comments on commit 0e3ca31

Please sign in to comment.