Store logs in MongoDB

Author:

Rohit Goswami

Overview

bless can store compressed log blobs in MongoDB alongside metadata (command, duration, timestamps, label, run_uuid).

Requires building with the mongodb feature.

Build with MongoDB support

cargo install bless --features mongodb
# or, from a checkout:
cargo build --release --features mongodb

The default install (cargo install bless) is gzip-only and does not include --use-mongodb.

Prerequisites

A running MongoDB instance. Set the connection URI:

export MONGODB_URI="mongodb://localhost:27017"

Usage

bless --use-mongodb --label nightly-build -- make test

This:

  1. Runs the command with logging as usual

  2. Connects to MongoDB

  3. Stores each gzip the logger opened, plus metadata, in the bless.commands collection

Override the destination with --db / --collection or MONGODB_DB / MONGODB_COLLECTION.

Persist uploads the gzip files the logger opened, not a reconstructed {label}_{uuid}.log.gz name. --use-mongodb with -o - is a config error: stdout-only mode writes no gzip to upload.

--use-mongodb --split inserts one document per stream. Both share run_uuid; stream is "stdout" or "stderr". Combined runs write stream as an empty string. A unique compound index on (run_uuid, stream) allows both documents; (label, start_time) is a non-unique index.

Querying stored runs

MONGODB_URI="mongodb://localhost:27017/" bless ls --use-mongodb
MONGODB_URI="mongodb://localhost:27017/" bless show <uuid-or-prefix> --use-mongodb
MONGODB_URI="mongodb://localhost:27017/" bless fetch <uuid-or-prefix> --use-mongodb -o run.log.gz

ls prints run_uuid, label, stream, storage, size_bytes, and start_time. fetch writes the embedded gzip_blob or streams the GridFS object; it does not call scripts/get_db_gzip.py.

Inspect with mongosh as well:

mongosh --eval 'db.getSiblingDB("bless").commands.find({label: "nightly-build"}).sort({start_time: -1}).limit(5)'

Gzip contents with --use-mongodb

Without --split, the gzip persisted to MongoDB is the same combined file as a local run: TRACE, DEBUG, INFO, WARN, and ERROR.

With --split, the stdout gzip is INFO and TRACE, the stderr gzip is WARN and ERROR, and both documents are stored.

How the blob is stored

Logs at or under 15 MiB are embedded as BSON Binary (~gzip_blob) on the metadata document. Larger logs, or any log when --force-gridfs is set, are streamed into GridFS. The metadata document then stores storage: "gridfs" and gzip_blob_id instead of the bytes.

Extracting a stored log

python scripts/get_db_gzip.py \
  --db-name bless \
  --collection-name commands \
  --query-field label \
  --query-value nightly-build \
  --output-file nightly-build.log.gz

The helper follows storage / gzip_blob_id to GridFS and otherwise reads the embedded gzip_blob.