write(array_merge([ 'source' => $source, 'action' => 'submit', 'status' => 'success', 'thesis_id' => $thesisId, 'identifier' => $identifier, 'author' => $authorName, ], $extras)); } /** * Log a duplicate-submission attempt. * * @param string $source 'admin' or 'partage' * @param string $authorName Author name from the incoming form * @param int $existingThesisId ID of the matched existing thesis * @param string $existingIdentifier Identifier of the matched thesis (e.g. "2025-003") * @param array $extras Additional context (e.g. share_slug) */ public function logDuplicate( string $source, string $authorName, int $existingThesisId, string $existingIdentifier, array $extras = [] ): void { $this->write(array_merge([ 'source' => $source, 'action' => 'submit', 'status' => 'duplicate', 'author' => $authorName, 'existing_thesis_id' => $existingThesisId, 'existing_identifier' => $existingIdentifier, ], $extras)); } /** * Log a failed thesis submission. * * @param string $source * @param string $errorMessage * @param array $extras */ public function logError(string $source, string $errorMessage, array $extras = []): void { $this->write(array_merge([ 'source' => $source, 'action' => 'submit', 'status' => 'error', 'error' => $errorMessage, ], $extras)); } /** * Write a structured log line — delegates to Monolog 'app' channel. */ private function write(array $entry): void { $entry['timestamp'] = date('c'); $entry['ip'] = $_SERVER['REMOTE_ADDR'] ?? 'unknown'; $entry['user_agent'] = $_SERVER['HTTP_USER_AGENT'] ?? ''; $line = json_encode($entry, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); Logger::get('app')->info($line); } }