Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cmd-log-parser

A Java command-line application that parses heterogeneous log files containing APM metrics, application events, and HTTP request logs, classifies each line, and writes per-type aggregations as JSON files.

The design uses Chain of Responsibility for log classification and Strategy for type-specific aggregation, so adding a new log type is a matter of dropping in three small classes — no modifications to existing code.


Table of Contents

  1. Features
  2. Requirements
  3. Build
  4. Usage
  5. Log Types
  6. Output Specification
  7. Project Structure
  8. Design
  9. Extending with a New Log Type
  10. Testing
  11. Troubleshooting

Features

  • Classifies and parses three log types from a single mixed file
  • Computes statistical aggregations:
    • APM: min, median, average, max per metric
    • Request: min, max, p50/p90/p95/p99 response times + 2XX/4XX/5XX counts per endpoint
    • Application: counts by severity level
  • Silently skips corrupted or unrecognized lines
  • Always writes all three output files, even when a type has zero entries (empty {})
  • Configurable output directory via --output
  • 42 unit tests covering parsing, aggregation, JSON writing, and end-to-end runs

Requirements

  • JDK 17+
  • Maven 3.6+
java -version
mvn -version

Build

# Compile + run tests + produce executable JAR
mvn clean package

This produces target/log-analyzer.jar (a shaded JAR with all dependencies bundled).

To skip tests:

mvn package -DskipTests

Usage

java -jar target/log-analyzer.jar --file <input.txt> [--output <dir>]

Arguments

Flag Required Default Description
--file yes Path to the input log file (.txt)
--output no output Directory where the three JSON files are written (created if missing)

Examples

# Default output directory: ./output/
java -jar target/log-analyzer.jar --file sample.txt

# Custom output directory
java -jar target/log-analyzer.jar --file sample.txt --output results

Console output

APM logs: 7 -> output/apm.json
Request logs: 7 -> output/request.json
Application logs: 7 -> output/application.json

The directory will contain:

  • apm.json
  • request.json
  • application.json

Log Types

All log lines follow a key=value format separated by spaces. Quoted values support spaces:

key1=value1 key2="value with spaces" key3=value3

1. APM logs

Detection: line contains both metric= AND value=

timestamp=2024-02-24T16:22:15Z metric=cpu_usage_percent host=webserver1 value=72

Required keys: metric, value (numeric)

2. Request logs

Detection: line contains both request_method= AND response_status=

timestamp=2024-02-24T16:22:25Z request_method=POST request_url="/api/update" response_status=202 response_time_ms=200 host=webserver1

Required keys: request_method, request_url, response_status (int), response_time_ms (numeric)

3. Application logs

Detection: line contains level= and is NOT an APM or Request line

timestamp=2024-02-24T16:22:20Z level=INFO message="Scheduled maintenance starting" host=webserver1

Required keys: level (normalized to uppercase)

Corrupted lines

Lines that don't match any handler, or that match a handler but fail field validation/parsing, are silently ignored.


Output Specification

apm.json

{
  "cpu_usage_percent": {
    "minimum": 65.0,
    "median": 68.5,
    "average": 68.5,
    "max": 72.0
  }
}

request.json

{
  "/api/status": {
    "response_times": {
      "min": 100.0,
      "max": 300.0,
      "50_percentile": 165.0,
      "90_percentile": 264.0,
      "95_percentile": 282.0,
      "99_percentile": 296.4
    },
    "status_codes": {
      "2XX": 3,
      "4XX": 0,
      "5XX": 1
    }
  }
}

Percentiles use linear interpolation: index = (p / 100) * (n - 1).

application.json

{
  "DEBUG": 1,
  "ERROR": 2,
  "INFO": 3,
  "WARNING": 1
}

Empty types

If no entries of a given type are found, the file is still written as { }.


Project Structure

cmd-log-parser/
├── pom.xml
├── README.md
├── .gitignore
├── sample.txt                              # 21-line sample mixed log
│
├── src/main/java/com/loganalyzer/
│   ├── LogAnalyzer.java                    # main entry point, CLI parsing
│   ├── model/
│   │   ├── LogEntry.java                   # abstract base
│   │   ├── APMLogEntry.java
│   │   ├── RequestLogEntry.java
│   │   └── ApplicationLogEntry.java
│   ├── handler/                            # Chain of Responsibility
│   │   ├── LogHandler.java                 # abstract handler + key/value parser
│   │   ├── APMLogHandler.java
│   │   ├── RequestLogHandler.java
│   │   └── ApplicationLogHandler.java
│   ├── strategy/                           # Strategy pattern
│   │   ├── AggregationStrategy.java        # interface
│   │   ├── APMAggregationStrategy.java
│   │   ├── RequestAggregationStrategy.java
│   │   └── ApplicationAggregationStrategy.java
│   └── util/
│       └── JSONWriter.java                 # Jackson pretty-print writer
│
└── src/test/java/com/loganalyzer/
    ├── HandlerTest.java                    # 12 tests
    ├── AggregationTest.java                # 4 tests
    ├── LogAnalyzerTest.java                # 10 tests
    ├── ModelTest.java                      # 10 tests
    └── JSONWriterTest.java                 # 6 tests

Design

Chain of Responsibility — classification

line → APMLogHandler → RequestLogHandler → ApplicationLogHandler → (drop)

Each handler decides at runtime whether it can handle a given line based on which keys are present. Lines that no handler accepts simply fall off the end of the chain.

Why:

  • Log type isn't tagged on the line — it must be inferred from content
  • Each handler has a single responsibility
  • New types are inserted as new links — existing handlers untouched
  • Invalid lines are handled by the same fall-through mechanism as unsupported types

Strategy — aggregation

interface AggregationStrategy<T extends LogEntry> {
    Map<String, Object> aggregate(List<T> entries);
}

Each log type has a fundamentally different aggregation algorithm (statistics vs. percentiles vs. counts). The strategy interface keeps that logic isolated and independently testable.

Trade-offs

Pros Cons
Open/Closed: add types without touching existing code More classes than a monolithic implementation
Each component testable in isolation Slight overhead for chain traversal
Type-safe aggregation via generics Requires familiarity with the patterns

Extending with a New Log Type

Suppose you want to add Security logs (threat_level=...):

  1. Modelmodel/SecurityLogEntry.java extending LogEntry
  2. Handlerhandler/SecurityLogHandler.java extending LogHandler, with canHandle() returning line.contains("threat_level=") and a parse() method
  3. Strategystrategy/SecurityAggregationStrategy.java implementing AggregationStrategy<SecurityLogEntry>
  4. Wire it up in LogAnalyzer.run() — append the handler to the chain and write its output:
appHandler.setNext(securityHandler);
// ...
writer.writeToFile(
    new SecurityAggregationStrategy().aggregate(toTyped(securityHandler.getEntries())),
    outputDir.resolve("security.json").toString());

Total: 3 new files + ~3 lines in LogAnalyzer.


Testing

# Run all tests
mvn test

# Run a single class
mvn test -Dtest=HandlerTest

# Run a single method
mvn test -Dtest=HandlerTest#chainRoutesEachLineToCorrectHandler

Coverage summary

Test class Tests Focus
HandlerTest 12 Parsing, chain routing, edge cases, malformed input
AggregationTest 4 Each strategy's calculations, empty inputs
LogAnalyzerTest 10 CLI arg parsing, end-to-end run, output dir handling
ModelTest 10 Constructors, getters, getType(), toString()
JSONWriterTest 6 File writing, null/empty maps, nested data, overwrites
Total 42

Troubleshooting

Symptom Cause / Fix
Usage: java -jar log-analyzer.jar --file ... --file flag missing
Error: file not found Check the path passed to --file
All output JSON files are { } Input file has no lines matching any handler — check formatting
mvn not found Install Maven 3.6+
Tests fail with ClassNotFoundException Run mvn clean test to rebuild

About

No description or website provided.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages