Skip to content

[Feature] Support structured JSON logs (Logback JSON format)#10

Open
MohammedGhallab wants to merge 1 commit into
AbaSheger:mainfrom
MohammedGhallab:main
Open

[Feature] Support structured JSON logs (Logback JSON format)#10
MohammedGhallab wants to merge 1 commit into
AbaSheger:mainfrom
MohammedGhallab:main

Conversation

@MohammedGhallab

@MohammedGhallab MohammedGhallab commented Jun 10, 2026

Copy link
Copy Markdown

Summary by CodeRabbit

  • New Features
    • Added support for JSON-formatted logs. The application now automatically detects JSON-structured log entries and converts them to plain text for seamless analysis and classification.
    • Improved log preprocessing to handle JSON logs and multiline output more efficiently before analysis.

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

PR adds JSON log preprocessing to StackLens: new JsonLogLine model and LogProcessor utility detect and convert JSON-formatted logs to plain text, which LogAnalyzer applies uniformly across all input sources before classification.

Changes

JSON Log Preprocessing

Layer / File(s) Summary
JSON Log Data Model
src/main/java/com/stacklens/JSONlogs/JsonLogLine.java
Defines JsonLogLine with Jackson annotations to deserialize JSON logs into timestamp, level, message, and stackTrace (mapped from stack_trace) fields.
JSON Detection and Conversion
src/main/java/com/stacklens/JSONlogs/LogProcessor.java
LogProcessor detects JSON boundary markers, deserializes via ObjectMapper into an internal JsonLogStructure, and converts to plain text by concatenating message and stack trace; returns original input on parse failure.
LogAnalyzer Preprocessing Integration
src/main/java/com/stacklens/analyzer/LogAnalyzer.java
LogAnalyzer adds LogProcessor dependency and preprocessLines helper to normalize all input (file lines, stream content, text) through JSON detection and plain-text conversion before classification; all three analysis entry points now use processed lines.

Sequence Diagram

sequenceDiagram
  participant Client as Analyzer Caller
  participant Analyzer as LogAnalyzer
  participant Processor as LogProcessor
  participant Mapper as ObjectMapper
  participant Classifier as IssueClassifier
  Client->>Analyzer: analyzeFile/Stream/Text
  Analyzer->>Processor: preprocessLines(raw lines)
  loop for each line
    Processor->>Processor: isJsonLog(line)
    alt JSON detected
      Processor->>Mapper: deserialize to JsonLogStructure
      Mapper->>Processor: message + stack_trace
      Processor->>Processor: concatenate to plain text
    else not JSON
      Processor->>Processor: return original line
    end
  end
  Analyzer->>Classifier: classify(processed lines)
  Classifier->>Analyzer: AnalysisResult
  Analyzer->>Client: return result
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

A rabbit hops through JSON streams,
Converting logs to cleaner dreams,
From curly braces, plain text flows—
Now StackLens sees what nature shows! 🐰📋

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning No pull request description was provided by the author; the template requires multiple sections including Description, Type of Change, Checklist, Testing, and Additional Notes. Add a comprehensive PR description following the template, explaining what the feature does, selecting the appropriate change type, completing the checklist, describing testing approach, and noting any additional context.
Docstring Coverage ⚠️ Warning Docstring coverage is 6.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding support for structured JSON logs in Logback format, which aligns with the new JsonLogLine, LogProcessor, and LogAnalyzer modifications.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/java/com/stacklens/JSONlogs/LogProcessor.java`:
- Around line 19-38: parseToPlainText drops structured fields and can produce an
empty string for JSON logs without a "message"; update the JsonLogStructure
record to include `@JsonProperty`("timestamp") String timestamp and
`@JsonProperty`("level") String level, then change parseToPlainText to incorporate
timestamp and level into the output (e.g. prefix or include them when building
plainText alongside message and stackTrace) so meaningful fields are preserved
for the classifier; ensure the method returns logLine only as a last-resort
fallback (i.e., if all extracted fields are null/empty) and not when
timestamp/level exist.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0678784a-b8e3-4b0f-aee5-eb81d1fb1c2f

📥 Commits

Reviewing files that changed from the base of the PR and between ae7282c and 3d33312.

📒 Files selected for processing (3)
  • src/main/java/com/stacklens/JSONlogs/JsonLogLine.java
  • src/main/java/com/stacklens/JSONlogs/LogProcessor.java
  • src/main/java/com/stacklens/analyzer/LogAnalyzer.java

Comment on lines +19 to +38
JsonLogStructure jsonLog = objectMapper.readValue(logLine, JsonLogStructure.class);
StringBuilder plainText = new StringBuilder();

if (jsonLog.message() != null) {
plainText.append(jsonLog.message());
}
if (jsonLog.stackTrace() != null && !jsonLog.stackTrace().isEmpty()) {
plainText.append("\n").append(jsonLog.stackTrace());
}
return plainText.toString();
} catch (Exception e) {
return logLine;
}
}

@JsonIgnoreProperties(ignoreUnknown = true)
record JsonLogStructure(
@JsonProperty("message") String message,
@JsonProperty("stack_trace") String stackTrace
) {}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve structured fields during normalization to avoid lossy classifier input.

parseToPlainText currently drops timestamp and level, and may return an empty string for valid JSON logs that lack message. Since preprocessing is now always applied before classification, this silently removes useful signal from the pipeline.

Suggested fix
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.databind.ObjectMapper;
@@
     public String parseToPlainText(String logLine) {
         try {
-            JsonLogStructure jsonLog = objectMapper.readValue(logLine, JsonLogStructure.class);
+            JsonLogLine jsonLog = objectMapper.readValue(logLine, JsonLogLine.class);
             StringBuilder plainText = new StringBuilder();
-            
-            if (jsonLog.message() != null) {
-                plainText.append(jsonLog.message());
+
+            if (jsonLog.getTimestamp() != null && !jsonLog.getTimestamp().isBlank()) {
+                plainText.append(jsonLog.getTimestamp());
             }
-            if (jsonLog.stackTrace() != null && !jsonLog.stackTrace().isEmpty()) {
-                plainText.append("\n").append(jsonLog.stackTrace());
+            if (jsonLog.getLevel() != null && !jsonLog.getLevel().isBlank()) {
+                if (plainText.length() > 0) plainText.append(" ");
+                plainText.append(jsonLog.getLevel());
             }
-            return plainText.toString();
+            if (jsonLog.getMessage() != null && !jsonLog.getMessage().isBlank()) {
+                if (plainText.length() > 0) plainText.append(" ");
+                plainText.append(jsonLog.getMessage());
+            }
+            if (jsonLog.getStackTrace() != null && !jsonLog.getStackTrace().isBlank()) {
+                if (plainText.length() > 0) plainText.append("\n");
+                plainText.append(jsonLog.getStackTrace());
+            }
+            return plainText.length() > 0 ? plainText.toString() : logLine;
         } catch (Exception e) {
             return logLine;
         }
     }
-
-    `@JsonIgnoreProperties`(ignoreUnknown = true)
-    record JsonLogStructure(
-        `@JsonProperty`("message") String message,
-        `@JsonProperty`("stack_trace") String stackTrace
-    ) {}
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/stacklens/JSONlogs/LogProcessor.java` around lines 19 - 38,
parseToPlainText drops structured fields and can produce an empty string for
JSON logs without a "message"; update the JsonLogStructure record to include
`@JsonProperty`("timestamp") String timestamp and `@JsonProperty`("level") String
level, then change parseToPlainText to incorporate timestamp and level into the
output (e.g. prefix or include them when building plainText alongside message
and stackTrace) so meaningful fields are preserved for the classifier; ensure
the method returns logLine only as a last-resort fallback (i.e., if all
extracted fields are null/empty) and not when timestamp/level exist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant