package de.slackspace.alfa.azure; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.microsoft.windowsazure.services.table.models.Entity; import de.slackspace.alfa.domain.DeploymentEntry; import de.slackspace.alfa.domain.DeploymentEntryMapper; import de.slackspace.alfa.domain.ElasticSearchEntry; import de.slackspace.alfa.domain.EntryMapper; import de.slackspace.alfa.domain.LogEntryMapper; import de.slackspace.alfa.domain.PerformanceCounterMapper; import de.slackspace.alfa.domain.TableResultPartial; import de.slackspace.alfa.elasticsearch.LogForwarder; import de.slackspace.alfa.exception.ConnectionException; import de.slackspace.alfa.properties.PropertyHandler; public class LogFetcher implements Runnable { private static final Logger LOGGER = LoggerFactory.getLogger(LogFetcher.class); private static final String LAST_PARTITION_KEY = PropertyHandler.LAST_PARTITION_KEY; private static final String LAST_ROW_KEY = PropertyHandler.LAST_ROW_KEY; private static final String LAST_PARTITION_KEY_PERFORMANCE = PropertyHandler.LAST_PARTITION_KEY_PERFORMANCE; private static final String LAST_ROW_KEY_PERFORMANCE = PropertyHandler.LAST_ROW_KEY_PERFORMANCE; private PropertyHandler propertyHandler; private AzureService service; private LogForwarder logForwarder; private int instance; private int pollingIntervalMinutes; private boolean fetchPerformanceCounters; private String accountName; private final LogEntryMapper logEntryMapper = new LogEntryMapper(); private final PerformanceCounterMapper performanceCounterMapper = new PerformanceCounterMapper(); public LogFetcher(PropertyHandler propertyHandler, LogForwarder logForwarder, AzureService azureService, int instance, int pollingIntervalMinutes, boolean fetchPerformanceCounters) { if(propertyHandler == null) { throw new IllegalArgumentException("Argument propertyHandler must not be null"); } if(logForwarder == null) { throw new IllegalArgumentException("Argument logForwarder must not be null"); } if(azureService == null) { throw new IllegalArgumentException("Argument azureService must not be null"); } this.propertyHandler = propertyHandler; this.logForwarder = logForwarder; this.service = azureService; this.instance = instance; this.pollingIntervalMinutes = pollingIntervalMinutes; this.fetchPerformanceCounters = fetchPerformanceCounters; this.accountName = propertyHandler.getProperty(PropertyHandler.ACCOUNT_NAME, instance); } public void run() { try { Map<String, String> map = getDeploymentMap(); // fetch logs fetchAndStoreEvents(map, "logs", LAST_ROW_KEY, LAST_PARTITION_KEY, AzureService.WADLOGSTABLE, logEntryMapper); if(fetchPerformanceCounters) { fetchAndStoreEvents(map, "performance counters", LAST_ROW_KEY_PERFORMANCE, LAST_PARTITION_KEY_PERFORMANCE, AzureService.PERFORMANCETABLE, performanceCounterMapper); } } catch(Throwable t) { LOGGER.error("An error occurred during fetching logs. Error was: ", t); } } private Map<String, String> getDeploymentMap() { Map<String, String> deploymentMap = new HashMap<String, String>(); TableResultPartial deploymentEntries = service.getDeploymentEntries(); for (Entity entity : deploymentEntries.getEntryList()) { DeploymentEntry deploymentEntry = DeploymentEntryMapper.mapToLogEntry(entity); deploymentMap.put(deploymentEntry.getDeploymentId(), deploymentEntry.getName()); } return deploymentMap; } private void fetchAndStoreEvents(Map<String, String> deploymentMap, String name, String lastRowKeyProperty, String lastPartitionKeyProperty, String storageTable, EntryMapper entryMapper) { String lastRowKey = propertyHandler.getProperty(lastRowKeyProperty, instance); String lastPartitionKey = propertyHandler.getProperty(lastPartitionKeyProperty, instance); if(LOGGER.isDebugEnabled()) { LOGGER.debug("[" + accountName + "] - Fetching " + name + " from partition: " + lastPartitionKey + " row: " + lastRowKey); } TableResultPartial tableResultPartial = service.getEntries(lastPartitionKey, lastRowKey, storageTable); removeDuplicateEvents(tableResultPartial, lastRowKey, lastPartitionKey); if(LOGGER.isDebugEnabled()) { LOGGER.debug("[" + accountName + "] - Found " + tableResultPartial.getEntryList().size() + " events."); } if(tableResultPartial.getEntryList().size() > 0) { storeEvents(tableResultPartial, deploymentMap, entryMapper); writeTraceProperties(tableResultPartial, lastRowKeyProperty, lastPartitionKeyProperty); } tableResultPartial.getEntryList().clear(); //if there are more entries in the azure table fetch until reaching the end if(tableResultPartial.getNextPartitionKey() != null) { fetchAndStoreEvents(deploymentMap, name, lastRowKeyProperty, lastPartitionKeyProperty, storageTable, entryMapper); } else { return; } } private void removeDuplicateEvents(TableResultPartial tableResultPartial, String lastRowKey, String lastPartitionKey) { if(tableResultPartial.getEntryList().size() > 0) { Entity firstEvent = tableResultPartial.getEntryList().get(0); if(firstEvent.getPartitionKey().equals(lastRowKey) && firstEvent.getRowKey().equals(lastPartitionKey)) { tableResultPartial.getEntryList().remove(firstEvent); } } } private void writeTraceProperties(TableResultPartial tableResultPartial, String lastRowKeyProperty, String lastPartitionKeyProperty) { //if we have reached the end of the azure table, remember the last fetched event if(tableResultPartial.getNextPartitionKey() == null) { Entity lastEvent = tableResultPartial.getEntryList().get(tableResultPartial.getEntryList().size() - 1); propertyHandler.setProperty(lastPartitionKeyProperty, lastEvent.getPartitionKey(), instance); propertyHandler.setProperty(lastRowKeyProperty, lastEvent.getRowKey(), instance); LogProgressInformation(lastEvent.getRowKey(), lastEvent.getPartitionKey()); } //if not at the end of the azure table, remember the partition key of the next event else { propertyHandler.setProperty(lastPartitionKeyProperty, tableResultPartial.getNextPartitionKey(), instance); propertyHandler.setProperty(lastRowKeyProperty, tableResultPartial.getNextRowKey(), instance); LogProgressInformation(tableResultPartial.getNextRowKey(), tableResultPartial.getNextPartitionKey()); } propertyHandler.writeProperties(); } private void LogProgressInformation(String lastRowKey, String lastPartitionKey) { if(LOGGER.isInfoEnabled()) { LOGGER.info("[" + accountName + "] - Fetched until partition: " + lastPartitionKey); LOGGER.info("[" + accountName + "] - Fetched until row: " + lastRowKey); } } private void storeEvents(TableResultPartial tableResultPartial, Map<String,String> deploymentMap, EntryMapper mapper) { if(LOGGER.isDebugEnabled()) { LOGGER.debug("[" + accountName + "] - Storing events into ES..."); } for (Entity entity : tableResultPartial.getEntryList()) { ElasticSearchEntry entry = mapper.mapToEntry(entity, deploymentMap); try { logForwarder.pushEvent(entry); } catch (ConnectionException e) { LOGGER.error("[" + accountName + "] - Could not write event to ES. Error was: ", e); } catch (IOException e) { LOGGER.error("[" + accountName + "] - Could not write create ES mapping. Error was: ", e); } } } public int getPollingIntervalMinutes() { return pollingIntervalMinutes; } }