fix: accept logs from unregistered agents using JWT claims
All checks were successful
CI / cleanup-branch (push) Has been skipped
CI / build (push) Successful in 1m24s
CI / docker (push) Successful in 1m7s
CI / deploy-feature (push) Has been skipped
CI / deploy (push) Successful in 38s

After server restart, agents send logs before re-registering. Instead
of dropping these logs, fall back to application and environment from
the JWT token claims. Only drops logs when neither registry nor JWT
provide an applicationId.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-12 21:29:05 +02:00
parent ce8a2a1525
commit 66248f6b1c

View File

@@ -2,15 +2,18 @@ package com.cameleer3.server.app.controller;
import com.cameleer3.common.model.LogEntry;
import com.cameleer3.server.app.metrics.ServerMetrics;
import com.cameleer3.server.app.security.JwtAuthenticationFilter;
import com.cameleer3.server.core.ingestion.BufferedLogEntry;
import java.util.List;
import com.cameleer3.server.core.ingestion.WriteBuffer;
import com.cameleer3.server.core.agent.AgentInfo;
import com.cameleer3.server.core.agent.AgentRegistryService;
import com.cameleer3.server.core.security.JwtService.JwtValidationResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import com.cameleer3.server.app.config.TenantProperties;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
@@ -49,7 +52,8 @@ public class LogIngestionController {
@Operation(summary = "Ingest application log entries",
description = "Accepts a batch of log entries from an agent. Entries are buffered and flushed periodically.")
@ApiResponse(responseCode = "202", description = "Logs accepted for indexing")
public ResponseEntity<Void> ingestLogs(@RequestBody List<LogEntry> entries) {
public ResponseEntity<Void> ingestLogs(@RequestBody List<LogEntry> entries,
HttpServletRequest request) {
String instanceId = extractAgentId();
if (instanceId == null || instanceId.isBlank()) {
log.warn("Log ingestion rejected: no agent identity in request (unauthenticated or missing principal)");
@@ -62,19 +66,28 @@ public class LogIngestionController {
return ResponseEntity.accepted().build();
}
String applicationId;
String environment;
AgentInfo agent = registryService.findById(instanceId);
if (agent == null) {
log.warn("Log ingestion from instance={}: agent not found in registry (not registered or expired). {} entries dropped.",
instanceId, entries.size());
serverMetrics.recordIngestionDrops("no_agent", entries.size());
return ResponseEntity.accepted().build();
if (agent != null) {
applicationId = agent.applicationId();
environment = agent.environmentId() != null ? agent.environmentId() : "default";
} else {
// Agent not yet in registry (e.g. server just restarted) — fall back to JWT claims
JwtValidationResult jwt = (JwtValidationResult) request.getAttribute(JwtAuthenticationFilter.JWT_RESULT_ATTR);
applicationId = jwt != null ? jwt.application() : null;
environment = jwt != null && jwt.environment() != null ? jwt.environment() : "default";
if (applicationId != null) {
log.debug("Log ingestion from instance={}: agent not in registry, using JWT claims (app={}, env={})",
instanceId, applicationId, environment);
}
}
String applicationId = agent.applicationId();
String environment = agent.environmentId() != null ? agent.environmentId() : "default";
if (applicationId == null || applicationId.isBlank()) {
log.warn("Log ingestion from instance={}: agent has no applicationId. {} entries dropped.", instanceId, entries.size());
log.warn("Log ingestion from instance={}: no applicationId from registry or JWT. {} entries dropped.",
instanceId, entries.size());
serverMetrics.recordIngestionDrops("no_agent", entries.size());
return ResponseEntity.accepted().build();
}