runspec-logops 0.1.0__py3-none-any.whl
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.
- runspec_logops/__init__.py +21 -0
- runspec_logops/bundle.py +30 -0
- runspec_logops/codemap.py +23 -0
- runspec_logops/digest.py +34 -0
- runspec_logops/runspec.toml +111 -0
- runspec_logops-0.1.0.dist-info/METADATA +11 -0
- runspec_logops-0.1.0.dist-info/RECORD +9 -0
- runspec_logops-0.1.0.dist-info/WHEEL +4 -0
- runspec_logops-0.1.0.dist-info/entry_points.txt +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""runspec-logops — log-condensing + code-mapping runnables.
|
|
2
|
+
|
|
3
|
+
The pure logic lives in the dependency-free ``runspec-logops-core`` package; the
|
|
4
|
+
modules here wrap each helper in a runspec runnable. The helper functions are
|
|
5
|
+
re-exported for convenience and back-compat (e.g. ``from runspec_logops import
|
|
6
|
+
summarize_log``). To import the helpers *without* surfacing any runnables in a
|
|
7
|
+
venv — the corporate-wrapping path — depend on ``runspec-logops-core`` directly
|
|
8
|
+
instead.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from runspec_logops_core import (
|
|
12
|
+
build_bundle,
|
|
13
|
+
map_trace_to_sources,
|
|
14
|
+
summarize_log,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"summarize_log",
|
|
19
|
+
"map_trace_to_sources",
|
|
20
|
+
"build_bundle",
|
|
21
|
+
]
|
runspec_logops/bundle.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
import runspec as rs
|
|
5
|
+
from runspec_logops_core import build_bundle, map_trace_to_sources, summarize_log
|
|
6
|
+
from runspec_logops_core.errors import SourceNotFoundError
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def main_bundle_digest() -> None:
|
|
10
|
+
spec = rs.parse("bundle-digest")
|
|
11
|
+
file_path = str(spec.file)
|
|
12
|
+
# Unset optionals resolve to an Arg wrapping value None; read .value so the
|
|
13
|
+
# absent case is a real None rather than the string "None".
|
|
14
|
+
repo = str(spec.repo) if spec.repo.value is not None else None
|
|
15
|
+
trace = str(spec.trace) if spec.trace.value is not None else None
|
|
16
|
+
destination = str(spec.destination)
|
|
17
|
+
top = int(spec.top)
|
|
18
|
+
try:
|
|
19
|
+
digest = summarize_log(file_path, top=top)
|
|
20
|
+
code_map = None
|
|
21
|
+
if trace and repo:
|
|
22
|
+
code_map = map_trace_to_sources(repo, trace)
|
|
23
|
+
result = build_bundle(digest, code_map, dest_dir=destination, metadata={"trace": trace})
|
|
24
|
+
print(json.dumps(result))
|
|
25
|
+
except SourceNotFoundError as e:
|
|
26
|
+
print(json.dumps({"error": str(e), "file": file_path}))
|
|
27
|
+
sys.exit(1)
|
|
28
|
+
except OSError as e:
|
|
29
|
+
print(json.dumps({"error": str(e), "file": file_path}))
|
|
30
|
+
sys.exit(1)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
import runspec as rs
|
|
5
|
+
from runspec_logops_core import map_trace_to_sources
|
|
6
|
+
from runspec_logops_core.errors import SourceNotFoundError
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def main_map_trace() -> None:
|
|
10
|
+
spec = rs.parse("map-trace")
|
|
11
|
+
repo = str(spec.repo)
|
|
12
|
+
trace = str(spec.trace)
|
|
13
|
+
context = int(spec.context)
|
|
14
|
+
max_files = int(spec.max_files)
|
|
15
|
+
try:
|
|
16
|
+
result = map_trace_to_sources(repo, trace, context=context, max_files=max_files)
|
|
17
|
+
print(json.dumps(result))
|
|
18
|
+
except SourceNotFoundError as e:
|
|
19
|
+
print(json.dumps({"error": str(e), "repo": repo}))
|
|
20
|
+
sys.exit(1)
|
|
21
|
+
except OSError as e:
|
|
22
|
+
print(json.dumps({"error": str(e), "repo": repo}))
|
|
23
|
+
sys.exit(1)
|
runspec_logops/digest.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
import runspec as rs
|
|
5
|
+
from runspec_logops_core import summarize_log
|
|
6
|
+
from runspec_logops_core.errors import SourceNotFoundError
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def main_summarize_log() -> None:
|
|
10
|
+
spec = rs.parse("summarize-log")
|
|
11
|
+
file_path = str(spec.file)
|
|
12
|
+
level = str(spec.level)
|
|
13
|
+
# Unset optionals resolve to an Arg wrapping value None; read .value so the
|
|
14
|
+
# absent case is a real None rather than the string "None".
|
|
15
|
+
since = spec.since.value
|
|
16
|
+
until = spec.until.value
|
|
17
|
+
top = int(spec.top)
|
|
18
|
+
max_sample_lines = int(spec.max_sample_lines)
|
|
19
|
+
try:
|
|
20
|
+
digest = summarize_log(
|
|
21
|
+
file_path,
|
|
22
|
+
level=level,
|
|
23
|
+
since=since,
|
|
24
|
+
until=until,
|
|
25
|
+
top=top,
|
|
26
|
+
max_sample_lines=max_sample_lines,
|
|
27
|
+
)
|
|
28
|
+
print(json.dumps(digest))
|
|
29
|
+
except SourceNotFoundError as e:
|
|
30
|
+
print(json.dumps({"error": str(e), "file": file_path}))
|
|
31
|
+
sys.exit(1)
|
|
32
|
+
except OSError as e:
|
|
33
|
+
print(json.dumps({"error": str(e), "file": file_path}))
|
|
34
|
+
sys.exit(1)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
[config]
|
|
2
|
+
autonomy-default = "autonomous"
|
|
3
|
+
|
|
4
|
+
[config.logging]
|
|
5
|
+
# Per-invocation log files: one {runnable}.{utc-ts}.{run_id}.log per run.
|
|
6
|
+
# Multi-writer safe for shared venvs (use `runspec logs` to view/compact/prune).
|
|
7
|
+
store = "per-run"
|
|
8
|
+
|
|
9
|
+
# ── Log condensing ─────────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
[summarize-log]
|
|
12
|
+
serve = ["local"]
|
|
13
|
+
description = "Condense a log file into a small digest: cluster repeated errors by signature and return the top-N distinct issues with counts, first/last timestamp and one sample each. The digest size is bounded regardless of how big the log is, so read this instead of the raw log."
|
|
14
|
+
autonomy = "autonomous"
|
|
15
|
+
output = "json"
|
|
16
|
+
|
|
17
|
+
[summarize-log.args.file]
|
|
18
|
+
type = "path"
|
|
19
|
+
short = "-f"
|
|
20
|
+
description = "Path to the log file to summarise"
|
|
21
|
+
|
|
22
|
+
[summarize-log.args.level]
|
|
23
|
+
type = "choice"
|
|
24
|
+
options = ["all", "warning", "error", "critical"]
|
|
25
|
+
default = "all"
|
|
26
|
+
description = "Only include events at or above this severity"
|
|
27
|
+
|
|
28
|
+
[summarize-log.args.since]
|
|
29
|
+
type = "str"
|
|
30
|
+
required = false
|
|
31
|
+
description = "Window start: ISO timestamp, 'now', or '<n> <unit> ago' (e.g. '2 hours ago')"
|
|
32
|
+
|
|
33
|
+
[summarize-log.args.until]
|
|
34
|
+
type = "str"
|
|
35
|
+
required = false
|
|
36
|
+
description = "Window end: ISO timestamp, 'now', or '<n> <unit> ago'"
|
|
37
|
+
|
|
38
|
+
[summarize-log.args.top]
|
|
39
|
+
type = "int"
|
|
40
|
+
short = "-n"
|
|
41
|
+
default = 10
|
|
42
|
+
description = "Number of distinct signatures to return (most frequent first)"
|
|
43
|
+
|
|
44
|
+
[summarize-log.args.max-sample-lines]
|
|
45
|
+
type = "int"
|
|
46
|
+
default = 20
|
|
47
|
+
description = "Max lines of the sample kept per signature"
|
|
48
|
+
|
|
49
|
+
# ── Trace → code ────────────────────────────────────────────────────────────────
|
|
50
|
+
|
|
51
|
+
[map-trace]
|
|
52
|
+
serve = ["local"]
|
|
53
|
+
description = "Map a stack trace (or a summarize-log signature like 'NullPointerException@com.acme.OrderSvc.price') to the few relevant source snippets in a checkout. Returns only the lines around each frame, never the whole repo."
|
|
54
|
+
autonomy = "autonomous"
|
|
55
|
+
output = "json"
|
|
56
|
+
|
|
57
|
+
[map-trace.args.repo]
|
|
58
|
+
type = "path"
|
|
59
|
+
short = "-r"
|
|
60
|
+
description = "Path to the source checkout to search"
|
|
61
|
+
|
|
62
|
+
[map-trace.args.trace]
|
|
63
|
+
type = "str"
|
|
64
|
+
short = "-t"
|
|
65
|
+
description = "The stack trace text, or a signature emitted by summarize-log"
|
|
66
|
+
|
|
67
|
+
[map-trace.args.context]
|
|
68
|
+
type = "int"
|
|
69
|
+
default = 8
|
|
70
|
+
description = "Lines of context to include around each resolved frame"
|
|
71
|
+
|
|
72
|
+
[map-trace.args.max-files]
|
|
73
|
+
type = "int"
|
|
74
|
+
default = 5
|
|
75
|
+
description = "Maximum number of frames/files to resolve"
|
|
76
|
+
|
|
77
|
+
# ── Bundle for transfer ─────────────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
[bundle-digest]
|
|
80
|
+
serve = ["local"]
|
|
81
|
+
description = "Summarise a log and (optionally) map a trace to code, then pack the digest + snippets + manifest into one small zip ready for transfer to the local machine. Writes a file."
|
|
82
|
+
autonomy = "confirm"
|
|
83
|
+
output = "json"
|
|
84
|
+
|
|
85
|
+
[bundle-digest.args.file]
|
|
86
|
+
type = "path"
|
|
87
|
+
short = "-f"
|
|
88
|
+
description = "Path to the log file"
|
|
89
|
+
|
|
90
|
+
[bundle-digest.args.repo]
|
|
91
|
+
type = "path"
|
|
92
|
+
short = "-r"
|
|
93
|
+
required = false
|
|
94
|
+
description = "Path to the source checkout for the optional code map (omit for a digest-only bundle)"
|
|
95
|
+
|
|
96
|
+
[bundle-digest.args.trace]
|
|
97
|
+
type = "str"
|
|
98
|
+
required = false
|
|
99
|
+
description = "Optional trace/signature to map to code; omit for a digest-only bundle"
|
|
100
|
+
|
|
101
|
+
[bundle-digest.args.destination]
|
|
102
|
+
type = "path"
|
|
103
|
+
short = "-d"
|
|
104
|
+
default = "/tmp"
|
|
105
|
+
description = "Directory to write the zip into"
|
|
106
|
+
|
|
107
|
+
[bundle-digest.args.top]
|
|
108
|
+
type = "int"
|
|
109
|
+
short = "-n"
|
|
110
|
+
default = 10
|
|
111
|
+
description = "Number of distinct signatures to include in the digest"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: runspec-logops
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Log-condensing + code-mapping runnables for runspec — cheap application-support triage from a log file
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Requires-Dist: runspec-logops-core>=0.1.0
|
|
7
|
+
Requires-Dist: runspec>=0.27.0
|
|
8
|
+
Provides-Extra: dev
|
|
9
|
+
Requires-Dist: mypy; extra == 'dev'
|
|
10
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
11
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
runspec_logops/__init__.py,sha256=1xs3HfbVgKYTioywOmbkTzSrwiA3fg4ATtgaFA4AtDk,662
|
|
2
|
+
runspec_logops/bundle.py,sha256=xV2tDfH3whI4s9Qd_sAsbZym1Gb2hCg9DwkBtZy9edk,1164
|
|
3
|
+
runspec_logops/codemap.py,sha256=pchw4dhXJdFTvmw2KCz20BGQiI5ij7D3MuzGciQyans,696
|
|
4
|
+
runspec_logops/digest.py,sha256=Dh4s6kfXyQtU7leqZcvCnBvR6rerXNvSxNnrxMAlFFE,1039
|
|
5
|
+
runspec_logops/runspec.toml,sha256=I9tlQEPQNKWhJN0bVoROfxi2zzatO3gxtKqjhu5ArMs,3980
|
|
6
|
+
runspec_logops-0.1.0.dist-info/METADATA,sha256=sXVPEMa04dxCASZ039caTiBhz_awsCdju-2OCBxw9aQ,404
|
|
7
|
+
runspec_logops-0.1.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
8
|
+
runspec_logops-0.1.0.dist-info/entry_points.txt,sha256=3V6ebdsUUmmbQZT-EN53sLXwKnu-aOSpug7oD_kIA6s,182
|
|
9
|
+
runspec_logops-0.1.0.dist-info/RECORD,,
|