Формат CSV и отправка пакета

Формат запроса

Запрос

Маршрут: POST /v2/tasks/{task_id}/logs. Параметр task_id в URL должен быть UUID. Передаётся непосредственно тело CSV, без JSON-обёртки и без multipart-загрузки. Для клиента следует указывать Content-Type: text/csv; charset=utf-8; сам обработчик разбирает тело как CSV и отдельно не проверяет этот заголовок. Авторизация подключена общими зависимостями маршрута v2.

Требования к сериализации:

  • Кодировка UTF-8. Входной BOM допускается, поскольку используется utf-8-sig.

  • Строки заголовка нет. Первая строка уже содержит сообщение лога.

  • В каждой записи строго 11 полей в указанном ниже порядке. Пустые поля не исключаются; пустое последнее поле также должно присутствовать.

  • Разделитель — запятая ,, символ кавычек — двойная кавычка ". Точка с запятой и табуляция не являются альтернативными разделителями.

  • Поле с запятой, кавычками или переводом строки заключается в двойные кавычки. Внутренняя двойная кавычка удваивается: " превращается в "". Это CSV-экранирование; оно не заменяет экранирование строк внутри JSON.

  • Перевод строки внутри правильно заключённого в кавычки поля допускается. Поэтому физическая строка файла не всегда равна одной записи CSV.

  • Для формирования пакета клиенту следует использовать LF между записями и завершать последнюю запись переводом строки. Сервер после разбора сам пересобирает пакет с LF и завершающим переводом строки.

  • Пустые записи и записи, все поля которых пустые или состоят из пробелов, пропускаются. Пакет без содержательных записей отклоняется.

Порядок полей:

id_connector,id_project,id_task,id_line,level,message_id,data,segment,count,timestamp,count_type

Эта строка показывает схему и не включается в тело запроса.

№

Поле

Обязательность и формат

Значение пустого поля

1

id_connector

Непустой идентификатор коннектора.

Ошибка пакета.

2

id_project

Непустой идентификатор проекта.

Ошибка пакета.

3

id_task

Идентификатор задачи; можно оставить пустым для подстановки из URL.

Заполняется значением task_id из URL.

4

id_line

Идентификатор строки проекта; необязателен.

Пустая строка в БД.

5

level

Одно из точных значений перечисления уровней, приведённых ниже.

Ошибка пакета.

6

message_id

Непустой устойчивый идентификатор сообщения. При повторе доставки не менять.

Ошибка пакета.

7

data

Необязательное JSON-значение. JSON внутри поля дополнительно экранируется по правилам CSV.

SQL NULL.

8

segment

Необязательное текстовое значение сегмента.

SQL NULL.

9

count

Необязательное целое в диапазоне от −2147483648 до 2147483647. Клиенту следует передавать десятичную запись без разделителей разрядов и дробной части.

SQL NULL.

10

timestamp

Необязательная дата и время, разбираемые datetime.fromisoformat(); допускается завершающий Z. Для однозначного времени клиенту следует передавать смещение или Z.

SQL NULL.

11

count_type

Необязательное JSON-значение. Отдельная прикладная схема этого значения здесь не проверяется.

SQL NULL.

Допустимые level, с учётом регистра:

Emergency Alert Critical Error Warning Notice Informational Debug

Значения Info, INFO, Warn не являются допустимыми заменами. id_log, ecc_dt и is_deleted клиент не передаёт: их назначает загрузчик. timestamp — время сообщения от клиента, ecc_dt — время записи в БД.

Проверка идентификаторов внутри CSV отличается от проверки UUID в URL: текущий CSV-валидатор проверяет непустоту обязательных строк, но не проверяет формат UUID каждого идентификатора, существование проекта или соответствие непустого id_task параметру URL. Клиент должен передавать согласованные идентификаторы. Нельзя считать эти связи подтверждёнными только фактом HTTP 201.

Пустое поле и текст NULL — разные значения. Например, пустой count становится SQL NULL, а строка NULL в count не является числом. JSON null в поле data является JSON-значением и отличается от пустого поля, преобразуемого в SQL NULL.

Пример пакета

Ниже условные идентификаторы, не относящиеся к реальной базе. В обеих записях id_task и id_line пустые. Последняя запятая сохраняет пустое одиннадцатое поле count_type.

11111111-1111-4111-8111-111111111111,22222222-2222-4222-8222-222222222222,,,Informational,log-20261020-000001,"{""text"":""Выгрузка начата""}",,0,2026-10-20T10:15:00+03:00, 11111111-1111-4111-8111-111111111111,22222222-2222-4222-8222-222222222222,,,Error,log-20261020-000002,"{""text"":""Ошибка, требуется повтор""}",,,2026-10-20T10:15:01+03:00,

Пример первой записи после разбора:

{ "id_connector": "11111111-1111-4111-8111-111111111111", "id_project": "22222222-2222-4222-8222-222222222222", "id_task": "значение UUID из URL запроса", "id_line": "", "level": "Informational", "message_id": "log-20261020-000001", "data": {"text": "Выгрузка начата"}, "segment": null, "count": 0, "timestamp": "2026-10-20T10:15:00+03:00", "count_type": null }

Это пояснение содержимого, а не альтернативный формат тела CSV-маршрута.

Проверка пакета и ответы

Весь запрос проверяется до передачи в буфер. Невалидная содержательная запись отклоняет весь входящий пакет: частичного принятия корректных соседних записей нет. Проверяются число колонок, обязательные поля, уровень, JSON, диапазон count и разбираемость timestamp. Максимальный размер тела задаёт V2_MAX_REQUEST_BYTES, по умолчанию 64 МиБ. Лимит числа записей V2_MAX_LOG_BATCH относится к JSON-маршруту; CSV-обработчик его не вызывает.

Ответ

Значение

201

Все содержательные записи проверены, нормализованы и помещены в память. data.count — число записей CSV, не число вставленных строк БД.

400

Пустой пакет, неверная кодировка/CSV/значение либо разрыв передачи тела клиентом.

413

Тело запроса превышает установленный предел.

503

Приём не готов, буфер или очередь перегружены либо включённая проверка обнаружила остановку загрузки. Для перегрузки/остановки передаётся Retry-After.

Ошибки авторизации обрабатываются зависимостями v2 до выполнения приёма. При повторной доставке того же сообщения сохраняется прежний message_id. Ответ 201 CSV-маршрута не содержит подтверждения долговременного хранения каждого message_id.

Отправка из PowerShell

Сохраните пакет из примера в файл logs.csv в UTF-8. Для запроса используйте JWT DCC, полученный при авторизации. Пример читает токен из окружения процесса:

$apiRoot = 'https://dcc.example.ru/api' $taskId = '33333333-3333-4333-8333-333333333333' $headers = @{ Authorization = "Bearer $env:DCC_ACCESS_TOKEN" } Invoke-RestMethod -Method Post ` -Uri "$apiRoot/v2/tasks/$taskId/logs" ` -Headers $headers ` -ContentType 'text/csv; charset=utf-8' ` -InFile '.\logs.csv'

Замените адрес и идентификаторы на значения своей инсталляции. Если публикация не использует /api, уберите этот префикс из apiRoot.

После 201 записи появятся в API чтения логов после обработки очереди. При сетевом таймауте исход приёма неизвестен: повторяйте прежние сообщения с теми же message_id. При 503 учитывайте Retry-After, если он присутствует. При 400 исправьте пакет, при 413 разделите его на меньшие пакеты.

Идентификатор сообщения уникален в пределах коннектора. Повтор с тем же (id_connector, message_id) пропускается при загрузке, а не обновляет ранее записанные данные. Не используйте один идентификатор для разных сообщений.