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 java.util.ArrayList;
19  import java.util.List;
20  import java.util.concurrent.ConcurrentLinkedQueue;
21  import java.util.concurrent.atomic.AtomicInteger;
22  
23  import org.codelibs.core.timer.TimeoutManager;
24  import org.codelibs.core.timer.TimeoutTask;
25  import org.codelibs.fess.timer.LogNotificationTarget;
26  import org.codelibs.fess.util.ComponentUtil;
27  
28  import jakarta.annotation.PostConstruct;
29  import jakarta.annotation.PreDestroy;
30  
31  /**
32   * Helper that manages the lifecycle of the log notification timer and
33   * buffers log notification events for periodic flushing to OpenSearch.
34   */
35  public class LogNotificationHelper {
36  
37      /**
38       * Default constructor.
39       */
40      public LogNotificationHelper() {
41          // Default constructor
42      }
43  
44      private TimeoutTask timeoutTask;
45      private LogNotificationTarget logNotificationTarget;
46      private final ConcurrentLinkedQueue<LogNotificationEvent> queue = new ConcurrentLinkedQueue<>();
47      private final AtomicInteger size = new AtomicInteger(0);
48  
49      /**
50       * Initializes the log notification timer.
51       */
52      @PostConstruct
53      public void init() {
54          logNotificationTarget = new LogNotificationTarget();
55          final int interval = ComponentUtil.getFessConfig().getLogNotificationFlushIntervalAsInteger();
56          timeoutTask = TimeoutManager.getInstance().addTimeoutTarget(logNotificationTarget, interval, true);
57      }
58  
59      /**
60       * Stops the log notification timer and performs a final flush.
61       */
62      @PreDestroy
63      public void destroy() {
64          if (timeoutTask != null) {
65              timeoutTask.cancel();
66          }
67          if (logNotificationTarget != null) {
68              logNotificationTarget.flush();
69          }
70      }
71  
72      /**
73       * Offers an event to the buffer. If the buffer exceeds the maximum size, the oldest event is dropped.
74       *
75       * @param event the log notification event to add
76       */
77      public void offer(final LogNotificationEvent event) {
78          int maxBufferSize;
79          try {
80              maxBufferSize = ComponentUtil.getFessConfig().getLogNotificationBufferSizeAsInteger();
81          } catch (final Exception e) {
82              maxBufferSize = 1000;
83          }
84          queue.offer(event);
85          if (size.incrementAndGet() > maxBufferSize) {
86              if (queue.poll() != null) {
87                  size.decrementAndGet();
88              }
89          }
90      }
91  
92      /**
93       * Drains all events from the buffer and returns them as a list.
94       *
95       * @return a list of all buffered events
96       */
97      public List<LogNotificationEvent> drainAll() {
98          final List<LogNotificationEvent> events = new ArrayList<>();
99          LogNotificationEvent event;
100         while ((event = queue.poll()) != null) {
101             events.add(event);
102             size.decrementAndGet();
103         }
104         return events;
105     }
106 
107     /**
108      * Represents a captured log event for notification.
109      */
110     public static class LogNotificationEvent {
111 
112         private final long timestamp;
113 
114         private final String level;
115 
116         private final String loggerName;
117 
118         private final String message;
119 
120         private final String throwable;
121 
122         /**
123          * Constructs a new LogNotificationEvent.
124          *
125          * @param timestamp the event timestamp in milliseconds
126          * @param level the log level name
127          * @param loggerName the logger name
128          * @param message the log message
129          * @param throwable the throwable string, or null
130          */
131         public LogNotificationEvent(final long timestamp, final String level, final String loggerName, final String message,
132                 final String throwable) {
133             this.timestamp = timestamp;
134             this.level = level;
135             this.loggerName = loggerName;
136             this.message = message;
137             this.throwable = throwable;
138         }
139 
140         /**
141          * Returns the event timestamp in milliseconds.
142          *
143          * @return the event timestamp in milliseconds
144          */
145         public long getTimestamp() {
146             return timestamp;
147         }
148 
149         /**
150          * Returns the log level name.
151          *
152          * @return the log level name
153          */
154         public String getLevel() {
155             return level;
156         }
157 
158         /**
159          * Returns the logger name.
160          *
161          * @return the logger name
162          */
163         public String getLoggerName() {
164             return loggerName;
165         }
166 
167         /**
168          * Returns the log message.
169          *
170          * @return the log message
171          */
172         public String getMessage() {
173             return message;
174         }
175 
176         /**
177          * Returns the throwable string.
178          *
179          * @return the throwable string, or null
180          */
181         public String getThrowable() {
182             return throwable;
183         }
184     }
185 }