001/* 002 * Copyright 2013 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.plugins.site.cache.monitoring.process.access; 017 018import java.io.BufferedReader; 019import java.io.File; 020import java.io.FileReader; 021import java.io.IOException; 022import java.text.DateFormat; 023import java.text.SimpleDateFormat; 024import java.util.ArrayList; 025import java.util.Collection; 026import java.util.Date; 027import java.util.List; 028import java.util.Locale; 029 030import org.apache.avalon.framework.activity.Disposable; 031import org.apache.avalon.framework.component.Component; 032import org.apache.avalon.framework.configuration.Configurable; 033import org.apache.avalon.framework.configuration.Configuration; 034import org.apache.avalon.framework.configuration.ConfigurationException; 035import org.apache.avalon.framework.logger.LogEnabled; 036import org.apache.avalon.framework.logger.Logger; 037import org.apache.avalon.framework.service.ServiceException; 038import org.apache.avalon.framework.service.ServiceManager; 039import org.apache.avalon.framework.service.Serviceable; 040import org.apache.commons.io.FilenameUtils; 041 042import org.ametys.core.util.DateUtils; 043import org.ametys.core.util.StringUtils; 044import org.ametys.plugins.site.cache.monitoring.process.access.impl.HTTPServerResourceAccess; 045import org.ametys.runtime.config.Config; 046 047 048/** 049 * Import HTTP server access log and pass them to the resource access monitor 050 */ 051public class HTTPServerAccessLogImporter implements Component, Configurable, Serviceable, Disposable, LogEnabled 052{ 053 /** Avalon ROLE. */ 054 public static final String ROLE = HTTPServerAccessLogImporter.class.getName(); 055 056 /** Logger */ 057 protected Logger _logger; 058 059 /** The resource access monitoring component */ 060 protected ResourceAccessComponent _resourceAccessComponent; 061 062 /** 063 * Date of the initialization of the component, to ensure that only newer 064 * log entries are importer 065 */ 066 protected Date _initializationDate; 067 068 private List<LogFileImporter> _logFileImporters; 069 070 private boolean _enabled; 071 072 @Override 073 public void service(ServiceManager manager) throws ServiceException 074 { 075 _resourceAccessComponent = (ResourceAccessComponent) manager.lookup(ResourceAccessComponent.ROLE); 076 } 077 078 @Override 079 public void enableLogging(Logger logger) 080 { 081 _logger = logger; 082 } 083 084 @Override 085 public void configure(Configuration configuration) throws ConfigurationException 086 { 087 _enabled = Config.getInstance().getValueAsBoolean("front.cache.monitoring.schedulers.enable"); 088 if (!_enabled) 089 { 090 return; 091 } 092 093 _initializationDate = new Date(); 094 _logFileImporters = new ArrayList<>(); 095 096 String frontConfig = Config.getInstance().getValueAsString("front.cache.monitoring.httpserver.log.paths"); 097 Collection<String> logFilePaths = StringUtils.stringToCollection(frontConfig); 098 099 configureLogFiles(logFilePaths); 100 101 initializeLogFileImporters(); 102 } 103 104 @Override 105 public void dispose() 106 { 107 _initializationDate = null; 108 _logFileImporters = null; 109 } 110 111 /** 112 * Scan the log files for each site importer. 113 */ 114 public synchronized void scanLogFiles() 115 { 116 if (!_enabled) 117 { 118 return; 119 } 120 121 for (LogFileImporter importer : _logFileImporters) 122 { 123 importer.importEntries(); 124 } 125 } 126 127 private void configureLogFiles(Collection<String> paths) 128 { 129 for (String path : paths) 130 { 131 String dirLog = FilenameUtils.getFullPathNoEndSeparator(path); 132 String logFilename = FilenameUtils.getName(path); 133 File logFile = new File(dirLog, logFilename); 134 135 _logFileImporters.add(new LogFileImporter(logFile)); 136 } 137 } 138 139 /** 140 * Initialize the log file importers. This method must not be called during 141 * the initialization of the component. Instead the call should be delayed, 142 * and made during the first import attempt. 143 */ 144 private void initializeLogFileImporters() 145 { 146 for (LogFileImporter importer : _logFileImporters) 147 { 148 importer.initialize(); 149 } 150 } 151 152 /** 153 * A log file importer. 154 * This class is able to import logs from an access log file. 155 */ 156 protected final class LogFileImporter 157 { 158 private final File _file; 159 // cached date format per importer, avoid synchronization issues while staying efficient 160 private final DateFormat _df = new SimpleDateFormat("dd/MMM/yyyy:HH:mm:ss Z", Locale.US); 161 162 private BufferedReader _br; 163 private boolean _initialized; 164 165 /** 166 * Constructor 167 * @param logFile the log file to import 168 */ 169 protected LogFileImporter(File logFile) 170 { 171 _file = logFile; 172 } 173 174 /** 175 * Initialize the log file importer. 176 */ 177 protected synchronized void initialize() 178 { 179 if (_initialized) 180 { 181 return; 182 } 183 184 try 185 { 186 _br = new BufferedReader(new FileReader(_file)); 187 skipEntriesUntilEOS(); 188 189 _initialized = true; 190 191 if (_logger.isInfoEnabled()) 192 { 193 String msg = String.format("The log file importer for the file '%s' is now initialized", _file); 194 _logger.info(msg); 195 } 196 } 197 catch (IOException e) 198 { 199 _logger.error("Exception when initializing the LogFileImporter for the file : '" + _file + "'", e); 200 } 201 } 202 203 /** 204 * Import new entries from the httpserver log file of this importer 205 */ 206 protected synchronized void importEntries() 207 { 208 if (!_initialized) 209 { 210 _logger.error("LogFileImporter not initialized. Unable to import the new entries for the file : '" + _file + "'"); 211 return; 212 } 213 214 try 215 { 216 scanLogEntries(); 217 } 218 catch (IOException e) 219 { 220 _logger.error("IOException while importing httpserver access log.", e); 221 } 222 catch (Exception e) 223 { 224 _logger.error("Unexpected exception while importing httpserver access log.", e); 225 } 226 } 227 228 /** 229 * Skip to the end of the buffered stream 230 * @throws IOException if something goes wrong during the entry skipping process 231 */ 232 private void skipEntriesUntilEOS() throws IOException 233 { 234 long start = System.currentTimeMillis(); 235 236 while (_br.readLine() != null) 237 { 238 // do nothing, just consume the buffered reader. 239 } 240 241 if (_logger.isDebugEnabled()) 242 { 243 String duration = DateUtils.formatDuration(System.currentTimeMillis() - start); 244 String msg = String.format("It tooks %s to consume the whole log file '%s'", duration, _file); 245 _logger.debug(msg); 246 } 247 } 248 249 private void scanLogEntries() throws IOException 250 { 251 int scanned = 0; 252 long start = System.currentTimeMillis(); 253 254 // Scan the new lines of the log file line by line. 255 // For each line, create a new HTTPServerResourceAccess, and pass it to 256 // the resourceAccessMonitor component if this entry must be 257 // persisted. 258 boolean eosReached = false; 259 while (_br.ready() && !eosReached) 260 { 261 String entry = _br.readLine(); 262 if (entry != null) 263 { 264 HTTPServerResourceAccess r = HTTPServerResourceAccess.createRecord(entry, _df); 265 if (r != null) 266 { 267 if (r.isOfInterest(_initializationDate)) 268 { 269 _resourceAccessComponent.addAccessRecord(r); 270 scanned++; 271 } 272 else 273 { 274 if (_logger.isDebugEnabled()) 275 { 276 _logger.debug(String.format("This httpserver access log entry has been filtered out : %s", r)); 277 } 278 } 279 } 280 } 281 else 282 { 283 // End of stream has been reached 284 eosReached = true; 285 } 286 } 287 288 if (_logger.isDebugEnabled()) 289 { 290 String durationStr = DateUtils.formatDuration(System.currentTimeMillis() - start); 291 _logger.debug(String.format("%s log entry(ies) scanned in %s", scanned, durationStr)); 292 } 293 } 294 } 295}