001/* 002 * Copyright 2012 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.runtime.plugins.admin.jvmstatus.monitoring; 017 018import java.io.File; 019import java.io.IOException; 020import java.util.ArrayList; 021import java.util.HashMap; 022import java.util.LinkedHashMap; 023import java.util.List; 024import java.util.Map; 025import java.util.Timer; 026import java.util.TimerTask; 027 028import org.apache.avalon.framework.activity.Disposable; 029import org.apache.avalon.framework.activity.Initializable; 030import org.apache.avalon.framework.component.Component; 031import org.apache.avalon.framework.logger.LogEnabled; 032import org.apache.avalon.framework.logger.Logger; 033import org.apache.avalon.framework.service.ServiceException; 034import org.apache.avalon.framework.service.ServiceManager; 035import org.apache.avalon.framework.service.Serviceable; 036import org.apache.commons.io.FileUtils; 037import org.rrd4j.core.RrdDb; 038 039import org.ametys.core.util.I18nUtils; 040import org.ametys.core.util.mail.SendMailHelper; 041import org.ametys.runtime.config.Config; 042import org.ametys.runtime.i18n.I18nizableText; 043import org.ametys.runtime.plugins.admin.jvmstatus.monitoring.alerts.AlertSampleManager; 044import org.ametys.runtime.plugins.admin.jvmstatus.monitoring.alerts.AlertSampleManager.Threshold; 045import org.ametys.runtime.servlet.RuntimeConfig; 046 047import jakarta.mail.MessagingException; 048 049/** 050 * {@link TimerTask} for creating and feeding RRDs files in order to 051 * produce graphs for monitoring: 052 * <ul> 053 * <li>JVM uptime 054 * <li>JVM memory status 055 * <li>JVM thread count 056 * <li>Servlet Engine request count 057 * <li>Servlet Engine session count 058 * </ul> 059 */ 060public class RRDsFeederTimerTask extends TimerTask implements Component, LogEnabled, Serviceable, Initializable, Disposable, MonitoringConstants 061{ 062 private static final String __CONFIG_ALERTS_ENABLED = "runtime.system.alerts.enable"; 063 private static final String __CONFIG_FROM_MAIL = "smtp.mail.from"; 064 private static final String __CONFIG_ADMIN_MAIL = "smtp.mail.sysadminto"; 065 066 private Logger _logger; 067 private MonitoringExtensionPoint _monitoringExtensionPoint; 068 private Timer _timer; 069 private String _rrdStoragePath; 070 private I18nUtils _i18nUtils; 071 072 /** Tells if there is a current alert, i.e. if we already sent an alert email 073 * This is a map {sampleManagerId -> datasourceName -> wasAlertedLastTime} */ 074 private Map<String, Map<String, Boolean>> _currentAlerts; 075 076 public void enableLogging(Logger logger) 077 { 078 _logger = logger; 079 } 080 081 public void service(ServiceManager manager) throws ServiceException 082 { 083 _monitoringExtensionPoint = (MonitoringExtensionPoint) manager.lookup(MonitoringExtensionPoint.ROLE); 084 _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE); 085 } 086 087 public void initialize() throws Exception 088 { 089 _rrdStoragePath = FileUtils.getFile(RuntimeConfig.getInstance().getAmetysHome(), RRD_STORAGE_DIRECTORY).getPath(); 090 091 _logger.debug("Starting timer"); 092 // Daemon thread 093 _timer = new Timer("RRDFeeder", true); 094 // Start in 30s and refresh each minutes 095 _timer.scheduleAtFixedRate(this, 30000, FEEDING_PERIOD * 1000); 096 097 _currentAlerts = new LinkedHashMap<>(); 098 } 099 100 @Override 101 public void run() 102 { 103 if (_logger.isDebugEnabled()) 104 { 105 _logger.debug("Time to collect data"); 106 } 107 108 for (String extensionId : _monitoringExtensionPoint.getExtensionsIds()) 109 { 110 SampleManager sampleManager = _monitoringExtensionPoint.getExtension(extensionId); 111 112 if (sampleManager != null) 113 { 114 String sampleName = sampleManager.getId(); 115 File rrdFile = new File(_rrdStoragePath, sampleName + RRD_EXT); 116 117 if (_logger.isDebugEnabled()) 118 { 119 _logger.debug("Collecting sample for: " + sampleName); 120 } 121 122 try (RrdDb rrdDb = RrdDb.of(rrdFile.getPath())) 123 { 124 Map<String, Object> collectedValues = sampleManager.collect(rrdDb.createSample()); 125 if (sampleManager instanceof AlertSampleManager) 126 { 127 _checkIfAlert((AlertSampleManager) sampleManager, collectedValues); 128 } 129 } 130 catch (Exception e) 131 { 132 _logger.error("Unable to collect sample for: " + sampleName, e); 133 } 134 } 135 } 136 } 137 138 private void _checkIfAlert(AlertSampleManager sampleManager, Map<String, Object> collectedValues) 139 { 140 if (Config.getInstance() == null) 141 { 142 return; 143 } 144 145 if (Config.getInstance().getValue(__CONFIG_ALERTS_ENABLED, true, false)) 146 { 147 if (_currentAlerts.get(sampleManager.getId()) == null) 148 { 149 _currentAlerts.put(sampleManager.getId(), new HashMap<>()); 150 } 151 152 Map<String, Threshold> thresholds = sampleManager.getThresholdValues(); 153 for (String datasourceName : thresholds.keySet()) 154 { 155 if (_currentAlerts.get(sampleManager.getId()).get(datasourceName) == null) 156 { 157 _currentAlerts.get(sampleManager.getId()).put(datasourceName, false); 158 } 159 160 Threshold threshold = thresholds.get(datasourceName); 161 if (threshold.isExceeded(collectedValues.get(datasourceName)) && !_currentAlerts.get(sampleManager.getId()).get(datasourceName)) 162 { 163 // Send the mail 164 _sendAlertMail(threshold.getMailSubject(), threshold.getMailBody(), collectedValues.get(datasourceName).toString(), threshold.getValue().toString()); 165 // Memorize to not send a mail again 166 _currentAlerts.get(sampleManager.getId()).put(datasourceName, true); 167 } 168 else if (!threshold.isExceeded(collectedValues.get(datasourceName)) && _currentAlerts.get(sampleManager.getId()).get(datasourceName)) 169 { 170 // Next check, we would possibly send a mail 171 _currentAlerts.get(sampleManager.getId()).put(datasourceName, false); 172 } 173 } 174 } 175 } 176 177 private void _sendAlertMail(I18nizableText subject, I18nizableText body, String currentValue, String thresholdValue) 178 { 179 Config config = Config.getInstance(); 180 String toMail = config.getValue(__CONFIG_ADMIN_MAIL); 181 String fromMail = config.getValue(__CONFIG_FROM_MAIL); 182 try 183 { 184 String subjectStr = _i18nUtils.translate(subject, "fr"); //FIXME fr hardcoded 185 186 List<String> bodyParams = new ArrayList<>(); 187 bodyParams.add(currentValue); 188 bodyParams.add(thresholdValue); 189 I18nizableText bodyWithParams = body.isI18n() ? new I18nizableText(body.getCatalogue(), body.getKey(), bodyParams) : body; 190 String bodyStr = _i18nUtils.translate(bodyWithParams, "fr"); 191 192 SendMailHelper.newMail() 193 .withSubject(subjectStr) 194 .withTextBody(bodyStr) 195 .withRecipient(toMail) 196 .withSender(fromMail) 197 .sendMail(); 198 } 199 catch (MessagingException | IOException e) 200 { 201 if (_logger.isWarnEnabled()) 202 { 203 _logger.warn("Could not send an alert e-mail to " + toMail, e); 204 } 205 } 206 } 207 208 public void dispose() 209 { 210 _logger = null; 211 _monitoringExtensionPoint = null; 212 _rrdStoragePath = null; 213 cancel(); 214 _timer.cancel(); 215 } 216}