# 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.
