Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ All notable user-visible changes should be recorded here.
### Added

- Added sanitized golden `report.md` / `report.json` regression fixtures to lock report contracts.
- Added `schema` and `schema_version` fields to `report.json` so downstream tooling can identify the report artifact contract.
- Expanded parser coverage for `Accepted publickey` and selected `pam_faillock` / `pam_sss` variants.
- Added compact host-level summaries for multi-host reports.
- Added optional CSV export for findings and warnings when explicitly requested.
Expand Down
7 changes: 7 additions & 0 deletions docs/report-artifacts.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Without `--csv`, LogLens does not create, overwrite, or delete existing CSV file
The JSON report keeps parser observability visible next to findings:

- `tool`
- `schema`
- `schema_version`
- `input`
- `input_mode`
- `assume_year` for syslog-style input when a year is supplied
Expand All @@ -44,6 +46,11 @@ Finding objects contain `rule_id`, `rule`, `subject_kind`, `subject`, `grouping_

Warning objects contain the original `line_number`, parser `category`, and parser `reason`.

`schema` and `schema_version` identify the report artifact contract, not the
application release. They are intended for downstream tooling that needs a
stable way to reject incompatible report shapes. The current JSON contract is
`loglens.report.v1` with `schema_version` set to `1`.

Parser failure categories are stable reviewer-facing buckets for unsupported
lines: `unknown_timestamp`, `unknown_program`,
`known_program_unknown_message`, `malformed_source_ip`, and
Expand Down
2 changes: 2 additions & 0 deletions src/report.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,8 @@ std::string render_json_report(const ReportData& data) {

output << "{\n";
output << " \"tool\": \"LogLens\",\n";
output << " \"schema\": \"loglens.report.v1\",\n";
output << " \"schema_version\": 1,\n";
output << " \"input\": \"" << escape_json(data.input_path.generic_string()) << "\",\n";
output << " \"input_mode\": \"" << to_string(data.parse_metadata.input_mode) << "\",\n";
if (data.parse_metadata.assume_year.has_value()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"tool": "LogLens",
"schema": "loglens.report.v1",
"schema_version": 1,
"input": "tests/fixtures/report_contracts/journalctl_short_full/input.log",
"input_mode": "journalctl_short_full",
"timezone_present": true,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"tool": "LogLens",
"schema": "loglens.report.v1",
"schema_version": 1,
"input": "tests/fixtures/report_contracts/multi_host_journalctl_short_full/input.log",
"input_mode": "journalctl_short_full",
"timezone_present": true,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"tool": "LogLens",
"schema": "loglens.report.v1",
"schema_version": 1,
"input": "tests/fixtures/report_contracts/multi_host_syslog_legacy/input.log",
"input_mode": "syslog_legacy",
"assume_year": 2026,
Expand Down
2 changes: 2 additions & 0 deletions tests/fixtures/report_contracts/syslog_legacy/report.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"tool": "LogLens",
"schema": "loglens.report.v1",
"schema_version": 1,
"input": "tests/fixtures/report_contracts/syslog_legacy/input.log",
"input_mode": "syslog_legacy",
"assume_year": 2026,
Expand Down
10 changes: 10 additions & 0 deletions tests/test_report.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,15 @@ void test_json_finding_includes_explainability_fields() {
"expected json finding to include evidence event ids");
}

void test_json_report_includes_schema_identity() {
const auto json = loglens::render_json_report(make_report_data());

expect(json.find("\"schema\": \"loglens.report.v1\"") != std::string::npos,
"expected json report to include schema identifier");
expect(json.find("\"schema_version\": 1") != std::string::npos,
"expected json report to include schema version");
}

void test_reports_include_total_input_line_count() {
auto data = make_report_data();
data.parser_quality.total_lines = 3;
Expand Down Expand Up @@ -349,6 +358,7 @@ int main() {
test_markdown_table_cells_escape_user_controlled_values();
test_json_escapes_generic_control_characters();
test_json_finding_includes_explainability_fields();
test_json_report_includes_schema_identity();
test_reports_include_total_input_line_count();
test_csv_neutralizes_formula_like_fields();
test_write_reports_fails_when_report_path_is_directory();
Expand Down
2 changes: 2 additions & 0 deletions tests/test_report_contracts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ std::vector<std::string> extract_json_contract_lines(const std::string& json) {
}

if (starts_with(line, "\"tool\": ")
|| starts_with(line, "\"schema\": ")
|| starts_with(line, "\"schema_version\": ")
|| starts_with(line, "\"input\": ")
|| starts_with(line, "\"input_mode\": ")
|| starts_with(line, "\"assume_year\": ")
Expand Down
Loading