Перейти к содержанию

9. Практический гайд: пишем утилиту анализа логов

← Архивы .jobsar · Оглавление · Далее: ошибки и отладка →

В этом гайде напишем небольшую CLI-утилиту, которая анализирует текст лога: считает HTTP-статусы, находит самый медленный запрос и печатает ошибки.

Шаг 1. Опишем параметры

Начнём с входного текста. Для примера зададим значение по умолчанию, чтобы скрипт можно было запустить без аргументов.

## Анализирует лог приложения и печатает краткий отчёт.
params {
    ## Текст лога. В реальном запуске его можно передать из файла или команды.
    $log String = "2026-06-23T09:00:01Z INFO api status=200 path=/health ms=4\n2026-06-23T09:00:05Z ERROR worker status=500 path=/jobs/import ms=302\n"
}

Шаг 2. Подготовим структуры данных

Нам нужны map для счётчиков, список ошибок и переменные для самого медленного запроса.

$status-counts = map {}
$slowest-path = ""
$slowest-ms = 0
$errors = []

Шаг 3. Разберём строки лога

Используем Split, Trim и регулярные выражения.

use [[ java.lang.Integer ]] as JInteger

for $line in $log.Split("\n") {
    $line = $line.Trim()
    if $line.Is-Empty() {
        continue
    }

    $status = re"status=([0-9]+)".Group($line, 1)
    $path = re"path=([^ ]+)".Group($line, 1)
    $ms-text = re"ms=([0-9]+)".Group($line, 1)

    if $status == null or $path == null or $ms-text == null {
        continue
    }

    if not $status-counts.Contains-Key($status) {
        $status-counts.Put($status, 0)
    }
    $status-counts.Put($status, $status-counts.Get($status) + 1)

    $ms = JInteger.parseInt($ms-text)
    if $ms > $slowest-ms {
        $slowest-ms = $ms
        $slowest-path = $path
    }

    if $line.Contains(" ERROR ") or $status.Starts-With("5") {
        $errors.Add($status + " " + $path + " " + $ms + "ms")
    }
}

Здесь Java-класс Integer используется для преобразования строки в число: JInteger.parseInt($ms-text).

Шаг 4. Напечатаем отчёт

println "Status summary"
for $status in $status-counts.Keys() {
    println "  " + $status + ": " + $status-counts.Get($status)
}

println "Slowest request: " + $slowest-path + " " + $slowest-ms + "ms"

if $errors.Is-Empty() {
    println "No errors found"
} else {
    println "Errors"
    for $error in $errors {
        println "  " + $error
    }
}

Полная версия

## Анализирует лог приложения и печатает краткий отчёт.
params {
    ## Текст лога.
    $log String = "2026-06-23T09:00:01Z INFO api status=200 path=/health ms=4\n2026-06-23T09:00:05Z ERROR worker status=500 path=/jobs/import ms=302\n"
}

use [[ java.lang.Integer ]] as JInteger

$status-counts = map {}
$slowest-path = ""
$slowest-ms = 0
$errors = []

for $line in $log.Split("\n") {
    $line = $line.Trim()
    if $line.Is-Empty() {
        continue
    }

    $status = re"status=([0-9]+)".Group($line, 1)
    $path = re"path=([^ ]+)".Group($line, 1)
    $ms-text = re"ms=([0-9]+)".Group($line, 1)

    if $status == null or $path == null or $ms-text == null {
        continue
    }

    if not $status-counts.Contains-Key($status) {
        $status-counts.Put($status, 0)
    }
    $status-counts.Put($status, $status-counts.Get($status) + 1)

    $ms = JInteger.parseInt($ms-text)
    if $ms > $slowest-ms {
        $slowest-ms = $ms
        $slowest-path = $path
    }

    if $line.Contains(" ERROR ") or $status.Starts-With("5") {
        $errors.Add($status + " " + $path + " " + $ms + "ms")
    }
}

println "Status summary"
for $status in $status-counts.Keys() {
    println "  " + $status + ": " + $status-counts.Get($status)
}

println "Slowest request: " + $slowest-path + " " + $slowest-ms + "ms"

if $errors.Is-Empty() {
    println "No errors found"
} else {
    println "Errors"
    for $error in $errors {
        println "  " + $error
    }
}

Запуск:

java -jar jobs.jar log-report.jobs

С передачей своего лога:

java -jar jobs.jar log-report.jobs --log "2026-06-23T10:00:00Z ERROR api status=503 path=/v1 ms=120"

Что здесь использовано

  • params для CLI-параметров;
  • map {} для счётчиков;
  • [] для списка ошибок;
  • for, if, continue для управления потоком;
  • re"...".Group(...) для извлечения данных;
  • Java-доступ через use [[ java.lang.Integer ]] as JInteger.

← Архивы .jobsar · Оглавление · Далее: ошибки и отладка →