CI-friendly policy enforcement for LLM API logs
- Overview
- Basic Usage
- Input Sources
- Policy Configuration
- Output Formats
- CI/CD Integration
- Privacy & Security
- Baseline Comparison
- Cost Control
- Prometheus Metrics
- Complete Examples
- Best Practices
The guard command is CrashLens's policy enforcement engine designed for CI/CD pipelines and production monitoring.
Key Features:
✅ Policy Enforcement - Evaluate logs against custom YAML rules
✅ CI/CD Ready - Fail builds on violations with --fail-on-violations
✅ Privacy First - --strip-pii and --no-content for safe sharing
✅ Multiple Outputs - JSON, Markdown, HTML, or plain text reports
✅ Organized Storage - Violations saved to policy-violations/ folder
✅ Backwards Compatible - Works with legacy policy-check command
✅ Prometheus Integration - Export metrics for monitoring
Syntax:
crashlens guard [LOGFILE] [OPTIONS]Quick Start:
# Basic guard with auto-discovered rules
crashlens guard logs.jsonl
# Fail build on violations (CI/CD)
crashlens guard logs.jsonl --fail-on-violations
# Privacy-safe report
crashlens guard logs.jsonl --strip-pii --no-content# Auto-discover rules from .crashlens/rules.yaml
crashlens guard logs.jsonl
# Specify rules file
crashlens guard logs.jsonl --rules policies/production.yaml
# Check multiple files
crashlens guard logs/*.jsonl --rules rules.yaml
# Check directory
crashlens guard logs/ --rules rules.yamlTest policies without failing builds:
# Show violations but don't exit with error code
crashlens guard logs.jsonl --dry-run
# Useful for testing new rules
crashlens guard logs.jsonl --rules new-rules.yaml --dry-run
# Preview with full output
crashlens guard logs.jsonl --dry-run --output jsonGuard automatically searches for rules in:
.crashlens/rules.yaml(current directory)~/.crashlens/rules.yaml(home directory)- Command-line
--rulesflag (highest priority)
# Uses .crashlens/rules.yaml if it exists
crashlens guard logs.jsonl
# Override with explicit path
crashlens guard logs.jsonl --rules custom-policy.yaml# Single file
crashlens guard logs.jsonl
# Multiple files
crashlens guard file1.jsonl file2.jsonl file3.jsonl
# Glob patterns
crashlens guard logs/*.jsonl# Scan entire directory
crashlens guard ./logs/
# Recursive scan
crashlens guard ./logs/ --rules policies/strict.yaml# From pipe
cat logs.jsonl | crashlens guard --stdin --rules rules.yaml
# From API
curl https://api.example.com/logs | crashlens guard --stdin --rules rules.yaml| Option | Type | Default | Description |
|---|---|---|---|
LOGFILE |
Path | Required | Path to JSONL log file or directory |
--stdin |
Flag | False | Read from standard input |
Basic YAML format:
version: 1
rules:
- id: RL001
description: "Block expensive models on simple tasks"
if:
and:
- input.model: "gpt-4"
- usage.completion_tokens:
'<': 10
action: fail_ci
severity: fatal
suggestion: "Use gpt-3.5-turbo for short completions"
- id: RL002
description: "Warn on high retry counts"
if:
metadata.retry_count:
'>': 3
action: warn
severity: error
suggestion: "Implement exponential backoff"Required fields:
id: Unique rule identifier (e.g.,RL001)description: Human-readable explanationif: Condition block (supports AND, OR, NOT)action:fail_ci,warn,blockseverity:fatal,error,warn,low
Condition operators:
- Comparison:
>,>=,<,<=,==,!= - List matching:
in: [...] - String matching:
regex: ... - Boolean logic:
and: [...],or: [...],not: {...}
| Option | Type | Default | Description |
|---|---|---|---|
--rules |
Path | Auto-discover | Path to rules YAML file |
-s, --suppress |
String | None | Suppress rule(s) by ID (repeatable) |
--severity |
Choice | error |
Min severity: warn, error, fatal |
Examples:
# Explicit rules file
crashlens guard logs.jsonl --rules policies/prod.yaml
# Suppress noisy rules
crashlens guard logs.jsonl --suppress RL001 --suppress RL002
# Only fatal violations
crashlens guard logs.jsonl --severity fatalStructured output for automation:
crashlens guard logs.jsonl --output jsonOutput structure:
{
"summary": {
"total_rules": 8,
"violations": 3,
"skipped_lines": 0,
"total_cost": 2.45,
"cost_cap": null,
"cost_cap_exceeded": false
},
"rules": {
"RL001": {
"count": 2,
"severity": "fatal",
"description": "High token usage on expensive models",
"examples": [
{
"timestamp": "2025-01-15T10:00:00Z",
"model": "gpt-4",
"tokens": 2500,
"cost": 0.15,
"reason": "usage.prompt_tokens=2500 (rule: >2000)"
}
]
}
}
}Human-readable reports:
crashlens guard logs.jsonl --output mdExample output:
# CrashLens Guard Report
- **Scanned**: `logs.jsonl`
- **Rules Checked**: 8
- **Violations Found**: 3
## Violations by Rule
### RL001: High token usage on expensive models (Fatal)
- **Count**: 2 violations
- **Severity**: Fatal
- **Examples**:
- Trace: abc123 (gpt-4, 2500 tokens)Web-ready reports with styling:
crashlens guard logs.jsonl --output htmlFeatures:
- Color-coded severity indicators
- Expandable violation details
- Copy-to-clipboard functionality
- Responsive design
Plain text for terminal:
crashlens guard logs.jsonl --output textSimple, clean output for quick review.
| Option | Type | Default | Description |
|---|---|---|---|
--output |
Choice | text |
Format: json, md, text, html |
--report-path |
Path | crashlens-report.json |
Path to write report |
Critical for production pipelines:
# Exit with code 2 if violations found
crashlens guard logs.jsonl --fail-on-violations
# Combined with severity threshold
crashlens guard logs.jsonl \
--fail-on-violations \
--severity fatalExit codes:
0: No violations (or violations but not failing)1: General error (file not found, invalid rules)2: Policy violations found with--fail-on-violations
name: Policy Enforcement
on: [push, pull_request]
jobs:
guard:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install CrashLens
run: pip install crashlens
- name: Fetch logs from Langfuse
env:
LANGFUSE_PUBLIC_KEY: ${{ secrets.LANGFUSE_PUBLIC_KEY }}
LANGFUSE_SECRET_KEY: ${{ secrets.LANGFUSE_SECRET_KEY }}
run: crashlens fetch-langfuse --hours-back 24 --output logs.jsonl
- name: Run guard
run: |
crashlens guard logs.jsonl \
--rules .crashlens/rules.yaml \
--fail-on-violations \
--output json \
--report-path ci-report.json
- name: Upload report
if: always()
uses: actions/upload-artifact@v4
with:
name: guard-report
path: ci-report.jsonguard:
stage: test
script:
- pip install crashlens
- crashlens guard logs.jsonl --rules .crashlens/rules.yaml --fail-on-violations
artifacts:
when: always
reports:
junit: crashlens-report.json| Option | Type | Default | Description |
|---|---|---|---|
--fail-on-violations |
Flag | False | Exit with code 2 on violations |
--dry-run |
Flag | False | Show violations without failing |
Remove sensitive data from reports:
# Remove emails, phones, SSNs, credit cards
crashlens guard logs.jsonl --strip-pii
# Supported PII types:
# - Email addresses
# - Phone numbers
# - Social Security Numbers
# - Credit card numbers
# - IP addressesExample:
Before: "Contact user@example.com for assistance"
After: "Contact [EMAIL_REDACTED] for assistance"
Exclude prompt/response text from reports:
# Remove all log content (prompts, completions)
crashlens guard logs.jsonl --no-content
# Shows only metadata and violation counts
# Perfect for sharing with external teamsHide trace IDs for internal sharing:
# Suppress trace IDs from output
crashlens guard logs.jsonl --summary-only
# Shows aggregated stats without identifying traces# Maximum privacy: no PII, no content, no trace IDs
crashlens guard logs.jsonl \
--strip-pii \
--no-content \
--summary-only \
--output json| Option | Type | Default | Description |
|---|---|---|---|
--strip-pii |
Flag | False | Remove PII (emails, phones, SSNs) |
--no-content |
Flag | False | Exclude log content (prompts/responses) |
--summary-only |
Flag | False | Suppress trace IDs |
All violations automatically saved to policy-violations/ directory:
policy-violations/
├── reports/
│ ├── 2025-01-15-violations.json # Daily reports
│ ├── 2025-01-15-violations.md
│ └── 2025-01-15-violations.html
├── traces/
│ ├── RL001/ # Organized by rule ID
│ │ ├── trace-abc123.json
│ │ └── trace-def456.json
│ └── RL002/
│ └── trace-ghi789.json
└── README.md # Auto-generated guide
Automatic organization:
reports/: Timestamped full reportstraces/: Individual violation details by ruleREADME.md: Index of violations
Control output location:
# Default location
crashlens guard logs.jsonl # → policy-violations/
# Custom report path
crashlens guard logs.jsonl --report-path ./ci-reports/violations.json
# Disable organized output (stdout only)
crashlens guard logs.jsonl --output text > report.txtCompare against historical baselines:
# Alert on 50% deviation from baseline
crashlens guard current.jsonl \
--baseline-logs historical.jsonl \
--baseline-deviation 0.50
# Stricter threshold (30%)
crashlens guard current.jsonl \
--baseline-logs last-week.jsonl \
--baseline-deviation 0.30Metrics analyzed:
- P95 latency
- P99 latency
- Average token usage
- Cost per request
- Error rates
Example alert:
⚠️ Baseline Deviation Detected
Metric: P95 Latency
Baseline: 1.2s
Current: 2.5s
Deviation: +108% (threshold: 50%)
| Option | Type | Default | Description |
|---|---|---|---|
--baseline-logs |
Path | None | Historical logs for comparison |
--baseline-deviation |
Float | 0.50 | Deviation threshold (0.50 = 50%) |
Fail builds if costs exceed budget:
# Maximum $100 per batch
crashlens guard logs.jsonl \
--cost-cap 100.0 \
--fail-on-violations
# Combined with other checks
crashlens guard logs.jsonl \
--cost-cap 50.0 \
--rules policies/budget.yaml \
--fail-on-violationsExit behavior:
- Exits with code 2 if cost exceeds cap
- Reports total cost in summary
- Detailed cost breakdown per trace
| Option | Type | Default | Description |
|---|---|---|---|
--cost-cap |
Float | None | Max total cost in USD |
Push to Prometheus Pushgateway:
# Basic metrics push
crashlens guard logs.jsonl --push-metrics
# Custom Pushgateway
crashlens guard logs.jsonl \
--push-metrics \
--pushgateway-url http://prometheus:9091 \
--metrics-job guard_productionAvailable in Prometheus:
# Total guard runs
crashlens_guard_runs_total{status="success"}
# Violations by severity
crashlens_guard_violations_total{severity="fatal", rule_id="RL001"}
# Logs processed
crashlens_guard_logs_processed_total
# Rules evaluated
crashlens_guard_rules_evaluated_total{rule_id="RL001"}
# Guard execution time
crashlens_guard_duration_seconds
# Last run timestamp
crashlens_guard_last_run_timestamp
| Option | Type | Default | Description |
|---|---|---|---|
--push-metrics |
Flag | False | Enable Prometheus metrics |
--pushgateway-url |
String | http://localhost:9091 |
Pushgateway URL |
--metrics-job |
String | crashlens-guard |
Job name |
# Alternative to CLI flags
export CRASHLENS_PUSHGATEWAY=http://prometheus:9091
export CRASHLENS_METRICS_JOB=guard_production
crashlens guard logs.jsonl --push-metricsRun custom scripts after guard completes:
# Post-process report
crashlens guard logs.jsonl \
--annotation-hook "./scripts/process-violations.sh"
# Hook receives report path as argument
# Example hook script:
# #!/bin/bash
# REPORT=$1
# echo "Processing $REPORT"
# python analyze.py "$REPORT"- Slack notifications: Send custom alerts
- JIRA integration: Create tickets for violations
- Data processing: Extract metrics for dashboards
- Report transformation: Convert to custom formats
- Alerting: Trigger PagerDuty/OpsGenie
| Option | Type | Default | Description |
|---|---|---|---|
--annotation-hook |
String | None | Command to run after report |
# Quick check with dry run
crashlens guard logs.jsonl --dry-run
# Check with specific rules
crashlens guard logs.jsonl --rules policies/dev.yaml
# Privacy-safe review
crashlens guard logs.jsonl --strip-pii --no-content# Fail on any fatal violations
crashlens guard logs.jsonl \
--rules .crashlens/rules.yaml \
--severity fatal \
--fail-on-violations \
--output json \
--report-path ci-report.json \
--push-metrics# Full monitoring with baseline comparison
crashlens guard current.jsonl \
--rules policies/production.yaml \
--baseline-logs historical.jsonl \
--baseline-deviation 0.30 \
--cost-cap 500.0 \
--fail-on-violations \
--strip-pii \
--summary-only \
--push-metrics \
--annotation-hook "./notify-team.sh"# Maximum privacy for external review
crashlens guard logs.jsonl \
--rules audit-rules.yaml \
--strip-pii \
--no-content \
--summary-only \
--output html \
--report-path external-audit.html# Gradually enforce new rules
crashlens guard logs.jsonl \
--rules new-rules.yaml \
--suppress OLD_RULE_001 \
--suppress OLD_RULE_002 \
--severity error \
--dry-run \
--output md# Fail if daily costs exceed budget
crashlens guard daily-logs.jsonl \
--cost-cap 100.0 \
--fail-on-violations \
--output json \
--report-path budget-check.json# Test new rules before enforcing
crashlens guard logs.jsonl --rules new-rules.yaml --dry-run
# Review output before enabling --fail-on-violationsRecommended thresholds per environment:
- Development:
--severity warn(catch everything) - Staging:
--severity error(block medium issues) - Production:
--severity fatal(only critical failures)
# Development
crashlens guard logs.jsonl --severity warn
# Production
crashlens guard logs.jsonl --severity fatal --fail-on-violations# Week 1: Add rule, suppress it
crashlens guard logs.jsonl --suppress NEW_RULE_001 --dry-run
# Week 2: Enable in dry-run mode
crashlens guard logs.jsonl --dry-run
# Week 3: Enable enforcement
crashlens guard logs.jsonl --fail-on-violationsFor any shared reports:
crashlens guard logs.jsonl \
--strip-pii \
--no-content \
--summary-onlyAlways include:
--fail-on-violations(gate builds)--output json(machine-readable)--push-metrics(observability)--report-path(artifact storage)
crashlens guard logs.jsonl \
--fail-on-violations \
--output json \
--report-path ci-report.json \
--push-metricsCreate suppression config:
# .crashlens/suppressions.yaml
suppressions:
- RL001 # Temporary during migration
- RL005 # Known issue, ticket #123Use with:
crashlens guard logs.jsonl \
--suppress RL001 \
--suppress RL005Refresh baselines regularly:
# Weekly baseline update
crashlens guard this-week.jsonl \
--baseline-logs last-week.jsonl \
--baseline-deviation 0.30
# Save current logs as next baseline
cp this-week.jsonl baselines/$(date +%Y-%m-%d).jsonlGuard is backwards compatible with policy-check:
# Old command (deprecated)
crashlens policy-check logs.jsonl --policy rules.yaml
# New command (recommended)
crashlens guard logs.jsonl --rules rules.yamlMigration path:
- Replace
policy-checkwithguard - Rename
--policyto--rules - All other options remain the same
Both formats supported:
# Old format (still works)
match:
model: gpt-4
tokens: ">1000"
# New format (recommended)
if:
and:
- input.model: gpt-4
- usage.prompt_tokens:
'>': 1000# Error: No rules file found
crashlens guard logs.jsonl
# Solution 1: Create .crashlens/rules.yaml
mkdir .crashlens
echo 'version: 1\nrules: []' > .crashlens/rules.yaml
# Solution 2: Specify rules explicitly
crashlens guard logs.jsonl --rules my-rules.yaml# Guard found violations but exited with 0
# → Add --fail-on-violations flag
crashlens guard logs.jsonl --fail-on-violations# Check severity threshold
crashlens guard logs.jsonl --severity warn # Show all
# Verify rule is not suppressed
crashlens guard logs.jsonl # Remove --suppress flags# For large log files, use sampling
crashlens guard large.jsonl --summary-only
# Or split into batches
split -l 10000 large.jsonl batch-
for f in batch-*; do
crashlens guard "$f" --fail-on-violations
done- Scan Command: Token waste detection
- Report Command: Cost digest reports
- CLI Reference: All commands
- Policy Guide: Policy configuration
- CI/CD Integration: Pipeline setup
- Observability: Prometheus & Grafana
Quick Start: crashlens guard logs.jsonl --fail-on-violations --strip-pii