4444use Exception ;
4545use InvalidArgumentException ;
4646use LogicException ;
47+ use Monolog \Handler \StreamHandler ;
48+ use Monolog \Level ;
49+ use Monolog \Logger ;
50+ use Monolog \Processor \ProcessorInterface ;
4751use Psr \Log \LoggerInterface ;
4852use Symfony \Component \DependencyInjection \Attribute \Autowire ;
53+ use Symfony \Component \DependencyInjection \Attribute \AutowireIterator ;
4954use Symfony \Component \HttpClient \Exception \TransportException ;
5055use Symfony \Component \HttpFoundation \File \UploadedFile ;
5156use Symfony \Component \PropertyAccess \Exception \UnexpectedTypeException ;
@@ -66,6 +71,8 @@ class ExternalContestSourceService
6671
6772 protected ?ExternalContestSource $ source = null ;
6873
74+ protected LoggerInterface $ logger ;
75+
6976 protected bool $ contestLoaded = false ;
7077 protected ?ContestData $ cachedContestData = null ;
7178 protected ?ApiInfo $ cachedApiInfoData = null ;
@@ -100,19 +107,28 @@ class ExternalContestSourceService
100107 'submission ' => [],
101108 ];
102109
110+ /**
111+ * @param \Traversable<ProcessorInterface> $logProcessors
112+ */
103113 public function __construct (
104114 HttpClientInterface $ httpClient ,
105115 protected readonly DOMJudgeService $ dj ,
106116 protected readonly EntityManagerInterface $ em ,
107- #[Autowire(service: 'monolog.logger.event-feed-importer ' )]
108- protected readonly LoggerInterface $ logger ,
109117 protected readonly ConfigurationService $ config ,
110118 protected readonly EventLogService $ eventLog ,
111119 protected readonly SubmissionService $ submissionService ,
112120 protected readonly ScoreboardService $ scoreboardService ,
113121 protected readonly SerializerInterface &DenormalizerInterface &NormalizerInterface $ serializer ,
122+ #[AutowireIterator(tag: 'monolog.processor ' )]
123+ protected readonly \Traversable $ logProcessors ,
124+ #[Autowire(service: 'monolog.processor.psr_log_message ' )]
125+ protected readonly ProcessorInterface $ psrLogMessageProcessor ,
126+ #[Autowire(param: 'kernel.logs_dir ' )]
127+ protected readonly string $ logDir ,
128+ #[Autowire(param: 'kernel.environment ' )]
129+ protected readonly string $ env ,
114130 #[Autowire('%domjudge.version% ' )]
115- string $ domjudgeVersion
131+ string $ domjudgeVersion,
116132 ) {
117133 $ clientOptions = [
118134 'headers ' => [
@@ -269,6 +285,8 @@ public function getLastReadEventId(): ?string
269285 */
270286 public function import (bool $ fromStart , array $ eventsToSkip , ?callable $ progressReporter = null ): bool
271287 {
288+ $ this ->configureLogger ();
289+
272290 // We need the verdicts to validate judgement-types.
273291 $ this ->verdicts = $ this ->config ->getVerdicts (['final ' , 'external ' ]);
274292
@@ -2087,4 +2105,19 @@ protected function removeWarning(EventType $eventType, ?string $entityId, string
20872105 $ this ->em ->flush ();
20882106 }
20892107 }
2108+
2109+ protected function configureLogger (): void
2110+ {
2111+ // Configure a logger to use a file per contest.
2112+ // For this we need to create our own logger.
2113+ // This code is based on what Symfony does with their default loggers.
2114+ $ name = sprintf ('event-feed-importer-%s ' , $ this ->getContestId ());
2115+ $ logFile = sprintf ('%s/%s.log ' , $ this ->logDir , $ name );
2116+ $ handler = new StreamHandler (
2117+ $ logFile ,
2118+ $ this ->env === 'dev ' ? Level::Debug : Level::Info,
2119+ );
2120+ $ handler ->pushProcessor ($ this ->psrLogMessageProcessor );
2121+ $ this ->logger = new Logger ($ name , [$ handler ], iterator_to_array ($ this ->logProcessors ));
2122+ }
20902123}
0 commit comments