春江暮客

春江暮客的个人学习分享网站

Stream gzip Logs with Python and Detect Truncation and CRC Errors

2026-10-14 Technology
Stream gzip Logs with Python and Detect Truncation and CRC Errors

A historical log is compressed as task.log.gz, and extracting it just to count errors consumes extra disk space. A partially copied compressed file can be more confusing: reading its first few lines may succeed even though the file is incomplete.

This beginner-to-intermediate tutorial uses the Python standard library to stream a gzip log, count matching lines, and verify exit codes for truncation and checksum failures. Use completed log backups that stay unchanged while being read.

1. Method: read compressed input and report after completion

The official Python gzip documentation describes text mode and encoding parameters for gzip.open(). We use rt with strict decoding; compression, file, and decoding errors fail the task.

The sequence is: check the gzip header → decompress and count lines → reach the end and close → print JSON. Do not stop after the first match: unread data can still contain a failure. Checking a header alone cannot replace a full read.

The existing rg and Python log triage tutorial covers filtering and structured reporting. This article adds direct reading of compressed backups. --contains ERROR is a case-sensitive literal substring search; it does not parse severity levels or HTTP status codes.

2. Prepare the environment and test log

Use Python 3.9 or newer, with no third-party dependencies. The examples were tested on Linux with Python 3.11.4; the commands work in bash or zsh.

  1. Create a fresh demo directory and run subsequent commands there. Choose a different directory name when repeating the experiment:
python3 --version
mkdir gzip-log-demo
cd gzip-log-demo
python3 - <<'PY_DATA'
import gzip
text = "INFO start\nERROR timeout\nINFO retry\nERROR failed"
with gzip.open("task.log.gz", "wt", encoding="utf-8", newline="\n") as file:
    file.write(text)
PY_DATA
  1. The test log contains four lines with no newline after the last one. Save the next section as summarize_gzip.py, or download the complete script.

3. Complete implementation

"""Count lines in a completed gzip log; emit JSON only after a full read."""
import argparse
import gzip
import json
from pathlib import Path
import sys
import zlib


def summarize(path, contains, encoding):
    lines = matches = 0
    with path.open("rb") as raw:
        if raw.read(2) != b"\x1f\x8b":
            raise gzip.BadGzipFile("missing gzip header")
        raw.seek(0)
        with gzip.open(raw, "rt", encoding=encoding, errors="strict") as stream:
            for line in stream:
                lines += 1
                if contains is None or contains in line:
                    matches += 1
    return {"file": str(path), "lines": lines, "matches": matches}


def main():
    parser = argparse.ArgumentParser(description=__doc__, allow_abbrev=False)
    parser.add_argument("file", type=Path, help="completed .gz text log")
    parser.add_argument("--contains", help="case-sensitive literal substring")
    parser.add_argument("--encoding", default="utf-8", help="text encoding (default: utf-8)")
    args = parser.parse_args()
    if args.contains == "":
        parser.error("contains must not be empty")
    try:
        result = summarize(args.file, args.contains, args.encoding)
    except (OSError, EOFError, zlib.error, UnicodeError, LookupError) as exc:
        print(f"ERROR: {args.file}: {exc}", file=sys.stderr)
        return 1
    print(json.dumps(result, ensure_ascii=False))
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

The code checks the two gzip signature bytes, then passes the same file object to the decompressor. Counting keeps counters rather than calling read() without a size or building a list of all lines. Memory still depends on the longest line and decompression buffers; this is not a fixed memory limit.

BadGzipFile inherits from OSError, so that branch handles both format and file-access errors. Invalid compressed data can also raise EOFError or zlib.error; both are caught. An invalid encoding name or strict decoding failure also returns 1.

4. Run and verify successful output

  1. Display help, then count errors, all lines, and an absent substring:
python3 summarize_gzip.py --help
python3 summarize_gzip.py task.log.gz --contains ERROR
python3 summarize_gzip.py task.log.gz
python3 summarize_gzip.py task.log.gz --contains WARNING

The second command prints:

{"file": "task.log.gz", "lines": 4, "matches": 2}

Without --contains, both lines and matches are 4. Searching for WARNING returns zero matches and exit code 0: no match is a normal result. Blank lines count, and a final line needs no trailing newline.

  1. Check the report fields:
python3 summarize_gzip.py task.log.gz --contains ERROR > report.json
python3 - <<'PY_CHECK'
import json
from pathlib import Path
result = json.loads(Path("report.json").read_text(encoding="utf-8"))
assert result == {"file": "task.log.gz", "lines": 4, "matches": 2}
print("OK: four lines, two matches")
PY_CHECK

Expect OK: four lines, two matches. These fields describe this complete read; they do not establish business-level completeness or authenticate the file’s source.

5. Create truncation and CRC failures

Modify only copies inside the demo directory. The following removes the eight-byte gzip trailer from one copy and changes a byte of the trailer CRC in another:

python3 - <<'PY_BAD'
from pathlib import Path
original = Path("task.log.gz").read_bytes()
Path("truncated.log.gz").write_bytes(original[:-8])
corrupt = bytearray(original)
corrupt[-8] ^= 1
Path("bad-crc.log.gz").write_bytes(corrupt)
PY_BAD
python3 summarize_gzip.py truncated.log.gz --contains ERROR > failed.json
printf 'exit=%s\n' "$?"
wc -c < failed.json
python3 summarize_gzip.py bad-crc.log.gz --contains ERROR
printf 'exit=%s\n' "$?"

Both runs should print ERROR: to standard error and show exit=1. wc should print 0, showing that the failed run wrote no successful JSON to standard output. Exact exception messages can vary with Python versions; do not rely on the full message to detect failure.

Additional checks covered a valid gzip containing empty content, CRLF, Unicode, concatenated gzip members, non-gzip input, missing files, and encoding errors. A full read still cannot prove all expected business records arrived: a complete compression of half a log can have a valid CRC.

The shell truncates report.json for > report.json before the program starts, including runs that later fail. To update an existing report, write a new temporary path and replace the report only after success:

if python3 summarize_gzip.py task.log.gz --contains ERROR > report.pending.json; then
  mv report.pending.json report.json
else
  rm -f report.pending.json
  printf 'Report update failed\n' >&2
fi

This snippet assumes a trusted demo directory and one runner. It preserves an existing report.json on failure; when embedding it in a job script, also return a nonzero exit code from the failure branch. See atomic JSON writes in Python for a fuller saving approach.

6. Troubleshooting and limits

Symptom Concrete action
missing gzip header Confirm the input is actually gzip; changing its extension does not change its format.
CRC check failed or premature end Wait for the source to finish, then copy it again. Compare source and copy checksums; do not treat partial counts as a complete report.
UnicodeDecodeError Check the log’s encoding and use --encoding gb18030 if appropriate; avoid ignoring decoding errors and silently losing content.
unknown encoding Correct the --encoding name, for example to utf-8.
Missing file or permission error Check the absolute path and access permissions on the file and parent directories.
A small file takes a long time Inspect uncompressed volume and line lengths; compressed size does not represent reading work.

Use completed backups rather than gzip files still being appended to. A .tar.gz decompresses to archive data, so it is not a plain-text log for this script. For creating and retaining rotated logs, see size-based Python log rotation; its default handler does not generate gzip backups automatically.

7. Summary

Reading gzip logs line by line avoids writing an extracted copy while keeping a simple JSON reporting interface. Verify full reads, strict decoding, and failure exit codes before connecting the tool to automation; replace existing reports only after success.

标签

1024 ablang adsense agents.md ai ai-agent ai-agents ai-seo algorithm amp antibodies apparmor argparse automation background backup bash batch-processing binarycif bioinformatics biopython blockchain boltz bootstrapping boxes bubblewrap c-index cca cdn checkpoint cli cloudflare codex cofoldarena copy cpu monitoring cron csv cuda curl data-leakage data-processing data-quality data-validation datascience datavisualization datetime debugging decimal deployment desktop-app devtools disk-space disown dns docker dovecot download economics electron encoding esm esm2 esm3 esmc esmfold2 fable faceswap fasta fastmcp faulthandler ffmpeg file-descriptors file-io flashppi flask folium frontend game generator git github-actions google google-research grep gzip harness hls html http hugo imports indexnow javascript jev json jsonl just k-means kaggle langfuse leecode linux list litellm llm llms.txt logging logs lollipop lsof m3u8 machine-learning macos manacher matplotlib mcp mirror mmcif model-evaluation monitoring mp3 mp4 mpnn multiomics mutation mysql nanobert nanobodies networkx nginx normalize numpy ollama omegatherm pandas password pathlib pdb pep-723 phaser pillow pip postfix preprocessing print probability protein-alignment protein-design protein-embeddings protein-interactions protein-language-models protein-stability protein-structure proxy pydantic pyecharts pyqt python python3 r raincloud reproducibility requests reservoir-sampling rfoptimization rg ripgrep rosettafold3 roundcube rrsi rsi rsync s-tui sampling scale scikit-learn scrapy screen seaborn security seo sequence-identity sha256 shell signals sklearn socket solana somaticsignatures spl sqlite ssh standardize static-site subprocess symlink sysadmin system-one tarfile tcp tensorflow tkinter tls toml trade tron tronpy troubleshooting turtle typesafe-ai ubuntu unicode usdt utf-8 uv vhhbert vite wallet webp wordcloud wordpress workflow yaml zipfile zoneinfo 迅雷解析

友情链接

其它