fix: accept logs from unregistered agents using JWT claims
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:
@@ -2,15 +2,18 @@ package com.cameleer3.server.app.controller;
|
|||||||
|
|
||||||
import com.cameleer3.common.model.LogEntry;
|
import com.cameleer3.common.model.LogEntry;
|
||||||
import com.cameleer3.server.app.metrics.ServerMetrics;
|
import com.cameleer3.server.app.metrics.ServerMetrics;
|
||||||
|
import com.cameleer3.server.app.security.JwtAuthenticationFilter;
|
||||||
import com.cameleer3.server.core.ingestion.BufferedLogEntry;
|
import com.cameleer3.server.core.ingestion.BufferedLogEntry;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import com.cameleer3.server.core.ingestion.WriteBuffer;
|
import com.cameleer3.server.core.ingestion.WriteBuffer;
|
||||||
import com.cameleer3.server.core.agent.AgentInfo;
|
import com.cameleer3.server.core.agent.AgentInfo;
|
||||||
import com.cameleer3.server.core.agent.AgentRegistryService;
|
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.Operation;
|
||||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import com.cameleer3.server.app.config.TenantProperties;
|
import com.cameleer3.server.app.config.TenantProperties;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
@@ -49,7 +52,8 @@ public class LogIngestionController {
|
|||||||
@Operation(summary = "Ingest application log entries",
|
@Operation(summary = "Ingest application log entries",
|
||||||
description = "Accepts a batch of log entries from an agent. Entries are buffered and flushed periodically.")
|
description = "Accepts a batch of log entries from an agent. Entries are buffered and flushed periodically.")
|
||||||
@ApiResponse(responseCode = "202", description = "Logs accepted for indexing")
|
@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();
|
String instanceId = extractAgentId();
|
||||||
if (instanceId == null || instanceId.isBlank()) {
|
if (instanceId == null || instanceId.isBlank()) {
|
||||||
log.warn("Log ingestion rejected: no agent identity in request (unauthenticated or missing principal)");
|
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();
|
return ResponseEntity.accepted().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String applicationId;
|
||||||
|
String environment;
|
||||||
|
|
||||||
AgentInfo agent = registryService.findById(instanceId);
|
AgentInfo agent = registryService.findById(instanceId);
|
||||||
if (agent == null) {
|
if (agent != null) {
|
||||||
log.warn("Log ingestion from instance={}: agent not found in registry (not registered or expired). {} entries dropped.",
|
applicationId = agent.applicationId();
|
||||||
instanceId, entries.size());
|
environment = agent.environmentId() != null ? agent.environmentId() : "default";
|
||||||
serverMetrics.recordIngestionDrops("no_agent", entries.size());
|
} else {
|
||||||
return ResponseEntity.accepted().build();
|
// 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()) {
|
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();
|
return ResponseEntity.accepted().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user