# Auditing a service and the volume gate ## Static audit Run from the checkout root of any Go service under `sources/project/`. Heuristic, not a linter: it flags short projection methods such as `toShortString()` as dumps, and it does not know about on-demand troubleshooting guards. Read what it prints; do not treat the counts as a gate on their own. ```python import re, glob files = [f for f in glob.glob('**/*.go', recursive=True) if not f.endswith('_test.go') and '/vendor/' not in f] info = dump = loop = noctx = 0 for path in files: depth, loops = 0, [] for i, line in enumerate(open(path, errors='ignore'), 1): stripped = line.strip() if re.search(r'\bfor .*\{\s*$', stripped): loops.append(depth) depth += line.count('{') - line.count('}') loops = [d for d in loops if d < depth] if re.search(r'logging\.Log(Info|Debug|Warning|Error|Fatal)\(', line): noctx += 1 print(f'noCtx {path}:{i}: {stripped[:100]}') if re.search(r'logging\.LogInfo(Ctx)?\(', line): info += 1 if '%+v' in line and not re.search(r'%\+v[^"]*"\s*,\s*len\(', line): dump += 1 print(f'dump {path}:{i}: {stripped[:100]}') if loops: loop += 1 print(f'loop {path}:{i}: {stripped[:100]}') print(f'INFO={info} dump={dump} loop={loop} noCtx={noctx}') ``` To exclude lines already behind an on-demand troubleshooting guard, track the brace depth of the block opened by `IsAlarmTroubleshootingActive(` and skip lines while inside it. In `gfiber-policy-executor` that moved the count from 77 INFO sites to 34 ungated ones, which is the number that matters. ### How to read the output | Signal | Meaning | |--------|---------| | high `dump` against low `INFO` | the few INFO lines the service has are the expensive kind | | any `loop` | a line scaling with item count rather than request count; the per-item result line is the one legitimate case | | `noCtx` | lines that cannot be attached to a work item | ## Volume gate Any change to logging on a high-volume path states its volume impact in the merge request. Measure the same scenario before and after, in the same namespace and window, using the `graylog-search` entry in [scripts/data/index.yaml](../../../scripts/data/index.yaml) with `--scope containers` and a container plus level filter, per [scripts/data/graylog-search.example.md](../../../scripts/data/graylog-search.example.md). Repeat for INFO, DEBUG, WARN and ERROR, then rerun on the branch build. | Metric | Before | After | Delta | |--------|--------|-------|-------| | INFO messages per run | | | | | INFO bytes per run | | | | | DEBUG messages per run | | | | | WARN and ERROR per run | | | | | Longest single INFO line, bytes | | | | Acceptance: INFO message count and INFO bytes must not increase. DEBUG is allowed to grow, since it is off in production. For SA services use the registered SVT cases from [skills/gfiber-svt-analysis/cases/index.yaml](../../gfiber-svt-analysis/cases/index.yaml). Services without an SVT case need a reference scenario agreed with the reviewer before the gate means anything. On the same run, confirm that a sample identifier from it is still findable at `LOG_LEVEL: INFO` with the SA alarm template from [queries/graylog/index.yaml](../../../queries/graylog/index.yaml). That is the regression the policy exists to prevent, and it is satisfied by the per-item result line rather than by anything new. ## Merge request checklist The hard rules in [levels.md](levels.md) double as the review checklist. In addition: - Every new INFO line matches one of the four INFO cases. - No new INFO line prints a collection, a struct or a body. - No new INFO line sits inside a loop over domain objects. - Every identifier list is capped. - Every call is the `Ctx` variant. - WARN and ERROR on failure paths carry the identifiers of the work they lost. - The summary line is written from a `defer` that survives a panic. - New metric labels come from a fixed vocabulary, with no identifiers in them. - `go vet` is clean and no line prints a pointer address or a `%!s` marker.