View Javadoc
1   /*
2    * Copyright 2012-2025 CodeLibs Project and the Others.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13   * either express or implied. See the License for the specific language
14   * governing permissions and limitations under the License.
15   */
16  package org.codelibs.fess.helper;
17  
18  import org.apache.logging.log4j.LogManager;
19  import org.apache.logging.log4j.Logger;
20  import org.codelibs.fess.app.service.FailureUrlService;
21  import org.codelibs.fess.crawler.CrawlerContext;
22  import org.codelibs.fess.crawler.entity.UrlQueue;
23  import org.codelibs.fess.crawler.exception.CrawlingAccessException;
24  import org.codelibs.fess.crawler.exception.MultipleCrawlingAccessException;
25  import org.codelibs.fess.crawler.helper.impl.LogHelperImpl;
26  import org.codelibs.fess.crawler.log.LogType;
27  import org.codelibs.fess.exception.ContainerNotAvailableException;
28  import org.codelibs.fess.helper.CrawlerStatsHelper.StatsAction;
29  import org.codelibs.fess.opensearch.config.exentity.CrawlingConfig;
30  import org.codelibs.fess.opensearch.config.exentity.FailureUrl;
31  import org.codelibs.fess.util.ComponentUtil;
32  
33  /**
34   * Helper class for crawler logging operations.
35   * Extends LogHelperImpl to provide specialized logging functionality for crawler operations,
36   * including failure URL tracking and crawler statistics integration.
37   */
38  public class CrawlerLogHelper extends LogHelperImpl {
39  
40      /**
41       * Creates a new instance of CrawlerLogHelper.
42       */
43      public CrawlerLogHelper() {
44          super();
45      }
46  
47      private static final Logger logger = LogManager.getLogger(CrawlerLogHelper.class);
48  
49      @Override
50      public void log(final LogType key, final Object... objs) {
51          if (!ComponentUtil.available()) {
52              if (logger.isDebugEnabled()) {
53                  logger.debug("container was destroyed.");
54              }
55              return;
56          }
57          super.log(key, objs);
58      }
59  
60      @Override
61      protected void processStartCrawling(final Object... objs) {
62          super.processStartCrawling(objs);
63          if (objs.length > 1 && objs[1] instanceof final UrlQueue<?> urlQueue) {
64              ComponentUtil.getCrawlerStatsHelper().begin(urlQueue);
65          }
66      }
67  
68      @Override
69      protected void processCleanupCrawling(final Object... objs) {
70          super.processCleanupCrawling(objs);
71          if (objs.length > 1 && objs[1] instanceof final UrlQueue<?> urlQueue) {
72              ComponentUtil.getCrawlerStatsHelper().done(urlQueue);
73          }
74      }
75  
76      @Override
77      protected void processProcessChildUrlByException(final Object... objs) {
78          super.processProcessChildUrlByException(objs);
79          if (objs.length > 1 && objs[1] instanceof final UrlQueue<?> urlQueue) {
80              ComponentUtil.getCrawlerStatsHelper().record(urlQueue, StatsAction.CHILD_URL);
81          }
82      }
83  
84      @Override
85      protected void processProcessChildUrlsByException(final Object... objs) {
86          super.processProcessChildUrlsByException(objs);
87          if (objs.length > 1 && objs[1] instanceof final UrlQueue<?> urlQueue) {
88              ComponentUtil.getCrawlerStatsHelper().record(urlQueue, StatsAction.CHILD_URLS);
89          }
90      }
91  
92      @Override
93      protected void processFinishedCrawling(final Object... objs) {
94          super.processFinishedCrawling(objs);
95          if (objs.length > 1 && objs[1] instanceof final UrlQueue<?> urlQueue) {
96              ComponentUtil.getCrawlerStatsHelper().record(urlQueue, StatsAction.FINISHED);
97          }
98      }
99  
100     @Override
101     protected void processCrawlingAccessException(final Object... objs) {
102         String failureUrlId = "?";
103         final CrawlerContext crawlerContext = (CrawlerContext) objs[0];
104         final UrlQueue<?> urlQueue = (UrlQueue<?>) objs[1];
105         final CrawlingAccessException cae = (CrawlingAccessException) objs[2];
106         try {
107             Throwable t = cae;
108             if (t instanceof final MultipleCrawlingAccessException mcae) {
109                 final Throwable[] causes = mcae.getCauses();
110                 if (causes.length > 0) {
111                     t = causes[causes.length - 1];
112                 }
113             }
114 
115             String errorName;
116             final Throwable cause = t.getCause();
117             if (cause != null) {
118                 errorName = cause.getClass().getCanonicalName();
119             } else {
120                 errorName = t.getClass().getCanonicalName();
121             }
122             final FailureUrl failureUrl = storeFailureUrl(crawlerContext, urlQueue, errorName, t);
123             if (failureUrl != null) {
124                 failureUrlId = failureUrl.getId();
125             }
126         } catch (final ContainerNotAvailableException e) {
127             if (logger.isDebugEnabled()) {
128                 logger.debug("container was destroyed.");
129             }
130             return;
131         } catch (final Exception e) {
132             if (!ComponentUtil.available()) {
133                 if (logger.isDebugEnabled()) {
134                     logger.debug("container was destroyed.");
135                 }
136                 return;
137             }
138             logger.warn("Failed to store failure url: url={}", urlQueue.getUrl(), e);
139         }
140 
141         if (cae.isDebugEnabled()) {
142             logger.debug("[{}] Crawling Access Exception: url={}", failureUrlId, urlQueue.getUrl(), cae);
143         } else if (cae.isInfoEnabled()) {
144             logger.info("[{}] {}", failureUrlId, cae.getMessage());
145         } else if (cae.isWarnEnabled()) {
146             logger.warn("[{}] Crawling Access Exception: url={}", failureUrlId, urlQueue.getUrl(), cae);
147         } else if (cae.isErrorEnabled()) {
148             logger.error("[{}] Crawling Access Exception: url={}", failureUrlId, urlQueue.getUrl(), cae);
149         }
150 
151         ComponentUtil.getCrawlerStatsHelper().record(urlQueue, StatsAction.ACCESS_EXCEPTION);
152     }
153 
154     @Override
155     protected void processCrawlingException(final Object... objs) {
156         final UrlQueue<?> urlQueue = objs.length > 1 && objs[1] instanceof UrlQueue<?> ? (UrlQueue<?>) objs[1] : null;
157         try {
158             final CrawlerContext crawlerContext = (CrawlerContext) objs[0];
159             final Throwable e = (Throwable) objs[2];
160 
161             storeFailureUrl(crawlerContext, urlQueue, e.getClass().getCanonicalName(), e);
162         } catch (final ContainerNotAvailableException e) {
163             if (logger.isDebugEnabled()) {
164                 logger.debug("container was destroyed.");
165             }
166             return;
167         } catch (final Exception e) {
168             if (!ComponentUtil.available()) {
169                 if (logger.isDebugEnabled()) {
170                     logger.debug("container was destroyed.");
171                 }
172                 return;
173             }
174             logger.warn("Failed to store failure url: url={}", urlQueue != null ? urlQueue.getUrl() : "unknown", e);
175         }
176 
177         super.processCrawlingException(objs);
178         if (urlQueue != null) {
179             ComponentUtil.getCrawlerStatsHelper().record(urlQueue, StatsAction.ACCESS_EXCEPTION);
180         }
181     }
182 
183     /**
184      * Stores a failure URL with error information for later analysis.
185      *
186      * @param crawlerContext the crawler context
187      * @param urlQueue the URL queue containing the failed URL
188      * @param errorName the name/type of the error
189      * @param e the throwable that caused the failure
190      * @return the stored FailureUrl entity
191      */
192     protected FailureUrl storeFailureUrl(final CrawlerContext crawlerContext, final UrlQueue<?> urlQueue, final String errorName,
193             final Throwable e) {
194 
195         final CrawlingConfig crawlingConfig = getCrawlingConfig(crawlerContext.getSessionId());
196         final String url = urlQueue.getUrl();
197 
198         final FailureUrlService failureUrlService = ComponentUtil.getComponent(FailureUrlService.class);
199         return failureUrlService.store(crawlingConfig, errorName, url, e);
200     }
201 
202     /**
203      * Gets the crawling configuration for the specified session ID.
204      *
205      * @param sessionCountId the session count ID
206      * @return the crawling configuration
207      */
208     protected CrawlingConfig getCrawlingConfig(final String sessionCountId) {
209         return ComponentUtil.getCrawlingConfigHelper().get(sessionCountId);
210     }
211 
212 }