private void GenerateLogMessageForFile(FileInfo fileInfo) { if (fileInfo.Name.StartsWith("irc.rizon.net")) { logger.Log( LogLevel.Info, "Skipping log file, that appears to be log from IRC (unsupported), file name: " + fileInfo.FullName, this, null); } else if (fileInfo.Name.StartsWith("test.")) { logger.Log( LogLevel.Info, "Skipping log file, that appears to be from test server (unsupported), file name: " + fileInfo.FullName, this, null); } else { logger.Log( LogLevel.Warn, "Skipping file that had parsing errors, file name: " + fileInfo.FullName, this, null); } }
public void Unsubscribe(string characterName, EventHandler <LogsMonitorEventArgs> eventHandler) { if (characterName == null) { logger.Log(LogLevel.Error, string.Format("Unsubscribe attempted with null characterName, handler details: {0} ", eventHandler.MethodInformationToString()), this, null); return; } try { var manager = GetManager(new CharacterName(characterName)); manager.RemoveSubscription(eventHandler); } catch (DataNotFoundException exception) { logger.Log(LogLevel.Info, string.Format( "Could not unsubscribe an event handler from logs monitor for character {0}. No manager found for this character. , handler details: {1}", characterName, eventHandler.MethodInformationToString()), this, exception); } }
public override ScanResult Execute(LogSearchParameters logSearchParameters, JobCancellationManager jobCancellationManager) { try { var scanner = logsScannerFactory.Create(logSearchParameters, jobCancellationManager); var results = scanner.Scan(); try { persistentLibrary.SaveChanged(); } catch (Exception exception) { logger.Log(LogLevel.Error, "Error at saving persistent data for WurmLogsHistory", this, exception); } return(results); } catch (Exception exception) { var canceledException = exception as OperationCanceledException; if (canceledException == null) { logger.Log(LogLevel.Error, string.Format("Error during scan job execution, search params: {0}", logSearchParameters), this, exception); } throw; } }
public IList <LogEntry> ParseLinesFromLogsScan(IReadOnlyList <string> lines, DateTime dayStamp) { List <LogEntry> result = new List <LogEntry>(lines.Count); DateTime? previousStamp = null; foreach (var line in lines) { if (IsLoggingStartedLine(line)) { continue; } // maybe just empty line (eg. happens on examining signs) if (string.IsNullOrWhiteSpace(line)) { continue; } TimeSpan?parsedTime = null; DateTime?currentStamp = null; try { parsedTime = ParsingHelper.GetTimestampFromLogLine(line); } catch (Exception exception) { logger.Log(LogLevel.Warn, "Found log line with unparseable timestamp, line may be ignored.", this, exception); } string source = ParsingHelper.TryParseSourceFromLogLine(line); string content = ParsingHelper.TryParseContentFromLogLine(line); if (parsedTime.HasValue) { currentStamp = new DateTime(dayStamp.Year, dayStamp.Month, dayStamp.Day, parsedTime.Value.Hours, parsedTime.Value.Minutes, parsedTime.Value.Seconds); } else if (previousStamp.HasValue) { currentStamp = previousStamp.Value; } if (currentStamp.HasValue) { previousStamp = currentStamp; LogEntry entry = new LogEntry(currentStamp.Value, source, content); result.Add(entry); } } return(result); }
async Task FindLatestDumpForServerGroup(ServerGroup serverGroup) { var beginDate = Time.Get.LocalNowOffset; var maxBackDate = Time.Get.LocalNow - MaxDaysBack; SkillDumpInfo[] dumps = new SkillDumpInfo[0]; if (skillDumpsDirectory.Exists) { dumps = skillDumpsDirectory.GetFiles().Select(ConvertFileInfoToSkillDumpInfo) .Where(info => info != null && info.Stamp > maxBackDate) .OrderByDescending(info => info.Stamp) .ToArray(); } SkillDump foundDump = null; foreach (var dumpInfo in dumps) { var server = await character.TryGetHistoricServerAtLogStampAsync(dumpInfo.Stamp).ConfigureAwait(false); if (server != null) { if (server.ServerGroup == serverGroup) { foundDump = new RealSkillDump(serverGroup, dumpInfo, logger); break; } } else { logger.Log(LogLevel.Info, "Could not identify server for skill dump: " + dumpInfo.FileInfo.FullName, this, null); } } if (foundDump != null) { latestSkillDumps[serverGroup] = foundDump; } else { // if nothing found, place a stub to prevent another file search latestSkillDumps[serverGroup] = new StubSkillDump(serverGroup); } lastRebuild = beginDate; }
/// <summary> /// Attempts to parse server name from a log entry. /// Null if parsing failed. /// </summary> /// <param name="logEntry"></param> /// <param name="logger">Optional, will log parsing errors.</param> /// <param name="sourceCharacterName">Optional, will be appended to log message.</param> /// <returns></returns> public static ServerStamp TryGetServerFromLogEntry(this LogEntry logEntry, IWurmApiLogger logger = null, CharacterName sourceCharacterName = null) { ServerStamp result = null; // attempt some faster matchers first, before trying actual parse if (Regex.IsMatch(logEntry.Content, @"other players are online", RegexOptions.Compiled)) { result = TryGetMatchResult(TryMatch1(logEntry), logEntry); if (result == null) { result = TryGetMatchResult(TryMatch2(logEntry), logEntry); } if (result == null) { logger?.Log( LogLevel.Warn, string.Format( "ServerHistoryProvider found 'other players are online' log line, but could not parse it. Character: {0} Entry: {1}", sourceCharacterName, logEntry), "ServerParsingHelper", null); } } return(result); }
void JobRunner() { while (true) { Thread.Sleep(CycleDelayMillis); if (stop) { return; } TaskHandle[] allTasks; lock (locker) { allTasks = tasks.ToArray(); } foreach (var taskHandle in allTasks) { try { taskHandle.TryExecute(); } catch (Exception exception) { logger.Log(LogLevel.Error, "Error during task execution: " + taskHandle.Description, this, exception); } } } }
public QueuedJobsSyncRunner([NotNull] JobExecutor <TJobContext, TResult> jobExecutor, IWurmApiLogger logger) { if (jobExecutor == null) { throw new ArgumentNullException(nameof(jobExecutor)); } this.jobExecutor = jobExecutor; searchJobTask = new Task((token) => { var internalCancellationToken = cancellationTokenSource.Token; while (true) { var signalled = jobSignaller.WaitOne(jobExecutor.IdleJobTreshhold); if (internalCancellationToken.IsCancellationRequested) { CancelAllRemainingJobs(); return; } if (!signalled) { try { jobExecutor.IdleJob(internalCancellationToken); } catch (Exception exception) { logger.Log(LogLevel.Error, "Regular job has thrown unhandled exception.", this, exception); } } ScanJob job; while (jobQueue.TryDequeue(out job)) { if (internalCancellationToken.IsCancellationRequested) { CancelAllRemainingJobs(); return; } try { var cancellationManager = new JobCancellationManager(internalCancellationToken, job.ExternalCancellationToken); var result = this.jobExecutor.Execute(job.JobContext, cancellationManager); job.SetResult(result); } catch (Exception exception) { job.SetException(exception); } } if (internalCancellationToken.IsCancellationRequested) { CancelAllRemainingJobs(); return; } } }, TaskCreationOptions.LongRunning); searchJobTask.Start(); }
public void TriggerInstantly <TEventArgs>(EventHandler <TEventArgs> handler, object source, TEventArgs args) where TEventArgs : EventArgs { eventMarshaller.Marshal(() => { try { handler?.Invoke(source, args); } catch (Exception exception) { logger.Log(LogLevel.Error, "EventMarshaller has thrown an unhandled exception on instant handler invocation", this, exception); } }); }
private List <LogEntry> GetEntries( IEnumerable <LogFileInfo> logFileInfos, CharacterMonthlyLogHeuristics heuristicsFileMap) { var parsingHelper = logFileParserFactory.Create(); List <LogEntry> result = new List <LogEntry>(); foreach (LogFileInfo logFileInfo in logFileInfos) { if (logFileInfo.LogFileDate.LogSavingType == LogSavingType.Monthly) { ParseMonthlyFile(heuristicsFileMap, logFileInfo, result, parsingHelper); } else if (logFileInfo.LogFileDate.LogSavingType == LogSavingType.Daily) { ParseDailyFile(logFileInfo, result, parsingHelper); } else { logger.Log( LogLevel.Warn, string.Format( "LogsScanner encountered and skipped file with unsupported saving type, type: {0}, file: {1}", logFileInfo.LogFileDate.LogSavingType, logFileInfo.FullPath), this, null); } cancellationManager.ThrowIfCancelled(); } return(result); }
public void Handle(ErrorContext errorContext) { logger.Log(LogLevel.Error, "Persistent object deserialization error, will ignore and use defaults, error details: " + errorContext.GetErrorDetailsAsString(), "WurmApi", null); errorContext.Decision = Decision.IgnoreErrorsAndReturnDefaultsForMissingData; }
public void Marshal(Action action) { Task.Factory.StartNew(action) .ContinueWith( task => logger.Log(LogLevel.Error, "Event handler has caused an unhandled exception", "WurmApi", task.Exception), TaskContinuationOptions.OnlyOnFaulted); }
internal WurmLogFiles(IWurmCharacterDirectories wurmCharacterDirectories, IWurmApiLogger logger, IWurmLogDefinitions wurmLogDefinitions, [NotNull] IInternalEventAggregator eventAggregator, [NotNull] IInternalEventInvoker internalEventInvoker, [NotNull] TaskManager taskManager, [NotNull] IWurmPaths wurmPaths) { if (wurmCharacterDirectories == null) { throw new ArgumentNullException(nameof(wurmCharacterDirectories)); } if (logger == null) { throw new ArgumentNullException(nameof(logger)); } if (wurmLogDefinitions == null) { throw new ArgumentNullException(nameof(wurmLogDefinitions)); } if (eventAggregator == null) { throw new ArgumentNullException(nameof(eventAggregator)); } if (internalEventInvoker == null) { throw new ArgumentNullException(nameof(internalEventInvoker)); } if (taskManager == null) { throw new ArgumentNullException(nameof(taskManager)); } if (wurmPaths == null) { throw new ArgumentNullException(nameof(wurmPaths)); } this.wurmCharacterDirectories = wurmCharacterDirectories; this.logger = logger; this.wurmLogDefinitions = wurmLogDefinitions; this.eventAggregator = eventAggregator; this.internalEventInvoker = internalEventInvoker; this.taskManager = taskManager; this.wurmPaths = wurmPaths; try { Refresh(); } catch (Exception exception) { logger.Log(LogLevel.Error, "Error at initial WurmLogFiles refresh", this, exception); } eventAggregator.Subscribe(this); taskHandle = new TaskHandle(Refresh, "WurmLogFiles refresh"); taskManager.Add(taskHandle); taskHandle.Trigger(); }
void ClearDir(string directoryPath, IWurmApiLogger logger) { var di = new DirectoryInfo(directoryPath); if (di.Exists) { di.Delete(recursive: true); logger.Log(LogLevel.Info, "Clearing cache completed for dir " + directoryPath, this, null); } }
Dictionary <string, float> ParseDump() { var fileLines = File.ReadAllLines(dumpInfo.FileInfo.FullName); Dictionary <string, float> skills = new Dictionary <string, float>(); var parser = new SkillEntryParser(logger); foreach (var line in fileLines) { if (line.StartsWith("Skills") || line.StartsWith("Characteristics") || line.StartsWith("Religion") || line.StartsWith("-----")) { continue; } var match = Regex.Match(line, @"(.+): (.+) .+ .+", RegexOptions.Compiled | RegexOptions.CultureInvariant); var skillName = match.Groups[1].Value.Trim(); if (string.IsNullOrEmpty(skillName)) { logger.Log(LogLevel.Error, string.Format("Unparseable skill name in dump file {0}, raw line: {1}", dumpInfo.FileInfo.FullName, line), this, null); continue; } var level = parser.TryParseFloatInvariant(match.Groups[2].Value); if (level == null) { logger.Log(LogLevel.Error, string.Format("Unparseable skill value in dump file {0}, raw line: {1}", dumpInfo.FileInfo.FullName, line), this, null); continue; } skills[WurmSkills.NormalizeSkillName(skillName)] = level.Value; } return(skills); }
internal WurmConfigs( [NotNull] IWurmConfigDirectories wurmConfigDirectories, [NotNull] IWurmApiLogger logger, [NotNull] IPublicEventInvoker publicEventInvoker, [NotNull] IInternalEventAggregator eventAggregator, [NotNull] TaskManager taskManager) { if (wurmConfigDirectories == null) { throw new ArgumentNullException(nameof(wurmConfigDirectories)); } if (logger == null) { throw new ArgumentNullException(nameof(logger)); } if (publicEventInvoker == null) { throw new ArgumentNullException(nameof(publicEventInvoker)); } if (eventAggregator == null) { throw new ArgumentNullException(nameof(eventAggregator)); } if (taskManager == null) { throw new ArgumentNullException(nameof(taskManager)); } this.wurmConfigDirectories = wurmConfigDirectories; this.logger = logger; this.publicEventInvoker = publicEventInvoker; this.eventAggregator = eventAggregator; this.taskManager = taskManager; onAvailableConfigsChanged = publicEventInvoker.Create( () => AvailableConfigsChanged.SafeInvoke(this, EventArgs.Empty), WurmApiTuningParams.PublicEventMarshallerDelay); onAnyConfigChanged = publicEventInvoker.Create( () => AnyConfigChanged.SafeInvoke(this, EventArgs.Empty), WurmApiTuningParams.PublicEventMarshallerDelay); try { Refresh(); } catch (Exception exception) { logger.Log(LogLevel.Error, "Error at initial configs update", this, exception); } taskHandle = new TaskHandle(Refresh, "WurmConfigs update"); taskManager.Add(taskHandle); eventAggregator.Subscribe(this); taskHandle.Trigger(); }
public void ProcessLine(string line, long lastReadLineStartPosition) { try { if (string.IsNullOrWhiteSpace(line)) { var currentDay = records.Any() ? records.Last().Day : 0; records.Add(new Record(currentDay, lastReadLineStartPosition)); } else if (line.StartsWith("Logging started", StringComparison.Ordinal)) { var lastDay = records.Any() ? records.Last().Day : 0; var dayStamp = ParsingHelper.GetDateFromLogFileLoggingStarted(line); ReadjustPreviousRecords(lastDay, dayStamp); records.Add(new Record(dayStamp.Day, lastReadLineStartPosition)); LastLogLineStamp = TimeSpan.Zero; } else { var currentDay = records.Any() ? records.Last().Day : 0; var lineStamp = ParsingHelper.GetTimestampFromLogLine(line); if (OverflowsToNextDay(lineStamp)) { LastLogLineStamp = TimeSpan.Zero; if (!(OverflowsBeyondToday(currentDay + 1) || OverflowsBeyondMaxMonth(currentDay + 1))) { currentDay++; } } else { LastLogLineStamp = lineStamp; } records.Add(new Record(currentDay, lastReadLineStartPosition)); } } catch (WurmApiException exception) { // this line must be added to records, because lastReadLineStartPosition var currentDay = records.Any() ? records.Last().Day : 0; records.Add(new Record(currentDay, lastReadLineStartPosition)); // ignore this line logger.Log( LogLevel.Warn, string.Format("Unexpected exception while parsing line: {0}", line), this, exception); } }
async void UpdateCurrentServer() { try { var server = await character.TryGetCurrentServerAsync().ConfigureAwait(false); if (server != null) { currentServer = server; } else { logger.Log(LogLevel.Warn, "Current server unknown for: " + character.Name.Capitalized, this, null); } } catch (Exception exception) { logger.Log(LogLevel.Error, "error on updating current server", this, exception); } await Task.Run(() => currentServerLookupFinished.TrySetResult(Time.Get.LocalNow)); }
internal WurmConfig(string gameSettingsFullPath, [NotNull] IPublicEventInvoker publicEventMarshaller, [NotNull] TaskManager taskManager, IWurmApiLogger logger) { if (gameSettingsFullPath == null) { throw new ArgumentNullException(nameof(gameSettingsFullPath)); } if (taskManager == null) { throw new ArgumentNullException(nameof(taskManager)); } gameSettingsFileInfo = new FileInfo(gameSettingsFullPath); if (gameSettingsFileInfo.Directory == null) { throw new WurmApiException("gameSettingsFileInfo.Directory is null, provided file raw path: " + gameSettingsFullPath); } Name = gameSettingsFileInfo.Directory.Name; this.taskManager = taskManager; onConfigChanged = publicEventMarshaller.Create(() => ConfigChanged.SafeInvoke(this, EventArgs.Empty), WurmApiTuningParams.PublicEventMarshallerDelay); configReader = new ConfigReader(this); try { Refresh(); } catch (Exception exception) { logger.Log(LogLevel.Error, "Error at initial config update: " + Name, this, exception); } configFileWatcher = new FileSystemWatcher(gameSettingsFileInfo.Directory.FullName) { Filter = gameSettingsFileInfo.Name, NotifyFilter = NotifyFilters.Size | NotifyFilters.LastWrite }; configFileWatcher.Changed += ConfigFileWatcherOnChanged; configFileWatcher.Created += ConfigFileWatcherOnChanged; configFileWatcher.Deleted += ConfigFileWatcherOnChanged; configFileWatcher.Renamed += ConfigFileWatcherOnChanged; configFileWatcher.EnableRaisingEvents = true; taskHandle = new TaskHandle(Refresh, "WurmConfig update: " + Name); taskManager.Add(taskHandle); taskHandle.Trigger(); }
private bool ShouldFileBeMonitored(LogFileInfo file, DateTime localNow) { if (file.LogFileDate.LogSavingType == LogSavingType.Daily) { return(file.LogFileDate.DateTime.Year == localNow.Year && file.LogFileDate.DateTime.Month == localNow.Month && file.LogFileDate.DateTime.Day == localNow.Day); } else if (file.LogFileDate.LogSavingType == LogSavingType.Monthly) { return(file.LogFileDate.DateTime.Year == localNow.Year && file.LogFileDate.DateTime.Month == localNow.Month); } else { if (!loggedUnknownFiles.Contains(file.FullPath)) { logger.Log(LogLevel.Warn, "CharacterLogsMonitorEngine found file with unknown saving type: " + file.FullPath, this, null); loggedUnknownFiles.Add(file.FullPath); } return(false); } }
public void ProcessLine(string line, long lastReadLineStartPosition) { AssertResultNotTaken(); LineCounter++; try { if (line.StartsWith("Logging started", StringComparison.Ordinal)) { var dayStamp = ParsingHelper.GetDateFromLogFileLoggingStarted(line); if (dayStamp.Day > CurrentDay) { CurrentDay = dayStamp.Day; LastLogLineStamp = TimeSpan.Zero; AdvanceDays(line, lastReadLineStartPosition); } else if (firstLoggingStartedFound && dayStamp.Day < CurrentDay) { //seems the logs have invalid timestamps, we need to flag for rollback RollbackHeuristics(dayStamp.Day); } firstLoggingStartedFound = true; } else { var lineStamp = ParsingHelper.GetTimestampFromLogLine(line); if (lineStamp < LastLogLineStamp && ParsingHelper.AreMoreThanOneHourAppartOnSameDay(lineStamp, LastLogLineStamp)) { CurrentDay++; LastLogLineStamp = TimeSpan.Zero; AdvanceDays(line, lastReadLineStartPosition); } else { LastLogLineStamp = lineStamp; } } } catch (WurmApiException exception) { // ignore this line logger.Log( LogLevel.Warn, string.Format("Unexpected exception while parsing line: {0}", line), this, exception); } }
/// <summary> /// </summary> /// <param name="fileInfo"></param> /// <returns></returns> public LogFileInfo Create(FileInfo fileInfo) { // should never throw var dateFromLogFileName = ParsingHelper.TryGetDateFromLogFileName(fileInfo.Name); var typeFromLogFileName = wurmLogDefinitions.TryGetTypeFromLogFileName(fileInfo.Name); string pmRecipient = null; if (typeFromLogFileName == LogType.Pm) { pmRecipient = ParsingHelper.TryParsePmRecipientFromFileName(fileInfo.Name); } bool parsingError = false; if (dateFromLogFileName.LogSavingType == LogSavingType.Unknown || dateFromLogFileName.DateTime == DateTime.MinValue) { parsingError = true; logger.Log(LogLevel.Info, "Detected issues with parsing log date for file " + fileInfo.FullName, this, null); } if (typeFromLogFileName == LogType.Unspecified) { parsingError = true; logger.Log(LogLevel.Info, "Detected issues with parsing log type for file " + fileInfo.FullName, this, null); } return(new LogFileInfo( fileInfo.FullName, fileInfo.Name, dateFromLogFileName, typeFromLogFileName, parsingError, pmRecipient)); }
async Task <IWurmServer> TryGetServerAtStamp(DateTime dateTime) { var result = await character.TryGetHistoricServerAtLogStampAsync(dateTime).ConfigureAwait(false); if (result == null) { logger.Log(LogLevel.Info, string.Format("Server could not be identified for character {0} at stamp {1}", character.Name.Capitalized, dateTime), this, null); return(null); } return(result); }
private void SyncWebData() { var allServers = wurmServerList.All.ToArray(); List <KeyValuePair <WurmServerInfo, Task <WebDataExtractionResult> > > jobs = new List <KeyValuePair <WurmServerInfo, Task <WebDataExtractionResult> > >(); foreach (var wurmServerInfo in allServers) { var info = wurmServerInfo; var task = Task.Factory.StartNew(() => extractor.Extract(info)); jobs.Add(new KeyValuePair <WurmServerInfo, Task <WebDataExtractionResult> >(wurmServerInfo, task)); } foreach (var job in jobs) { try { var result = job.Value.Result; dataCache[result.ServerName] = new TimeDetails() { ServerDate = new ServerDateStamped() { WurmDateTime = result.WurmDateTime, Stamp = result.LastUpdated }, ServerUptime = new ServerUptimeStamped() { Uptime = result.ServerUptime, Stamp = result.LastUpdated } }; } catch (Exception exception) { logger.Log( LogLevel.Warn, "WurmServer-LiveLogs Error at web data extraction for server: " + job.Key, this, exception); } } lastSync = Time.Get.LocalNowOffset; }
public void ReportIssue(TKey key) { int count; lock (locker) { count = issueCounts.GetOrAdd(key, () => 0); count++; issueCounts[key] = count; } if (count >= issueTreshhold) { logger.Log(LogLevel.Warn, string.Format("{1} > Adding key {0} to blacklist because at least {2} issues were reported. " + "List will be reset after application restart.", key, description, count), this, null); } }
List <Exception> AddNewFileManagers(string[] allDirNames, IReadOnlyDictionary <CharacterName, WurmCharacterLogFiles> oldMap, Dictionary <CharacterName, WurmCharacterLogFiles> newMap) { List <Exception> exceptions = new List <Exception>(); foreach (var dirName in allDirNames) { var charName = new CharacterName(dirName); WurmCharacterLogFiles logFiles; if (!oldMap.TryGetValue(charName, out logFiles)) { try { var fullDirPathForCharacter = wurmCharacterDirectories.GetFullDirPathForCharacter(charName); var logsDirPath = Path.Combine(fullDirPathForCharacter, wurmPaths.LogsDirName); logFiles = new WurmCharacterLogFiles( charName, logsDirPath, logger, new LogFileInfoFactory(wurmLogDefinitions, logger), internalEventInvoker, taskManager); newMap.Add(charName, logFiles); } catch (Exception exception) { logger.Log(LogLevel.Error, "Error at WurmLogFiles refresh for character: " + charName, this, exception); exceptions.Add(exception); } } else { newMap.Add(charName, logFiles); } } return(exceptions); }
void Refresh() { var di = new DirectoryInfo(DirectoryFullPath); var allDirs = di.GetDirectories().Where(info => (info.Attributes & FileAttributes.Hidden) == 0); var newMap = new Dictionary <string, string>(); foreach (var directoryInfo in allDirs) { if (directoryBlacklist.IsOnBlacklist(directoryInfo.FullName)) { continue; } try { validateDirectory(directoryInfo.FullName, wurmPaths); newMap.Add(directoryInfo.Name.ToUpperInvariant(), directoryInfo.FullName); } catch (ValidationException exception) { directoryBlacklist.ReportIssue(directoryInfo.FullName); logger.Log(LogLevel.Warn, "Validation issue", this, exception); // todo: need to log this as warning, solving with quick solution // consider: extend AggregateException to carry logging level and handle that universally in TaskManager task.SetErrorAndRetrigger(); } } var oldDirs = dirNameToFullPathMap.Select(pair => pair.Key).OrderBy(s => s).ToArray(); var newDirs = newMap.Select(pair => pair.Key).OrderBy(s => s).ToArray(); var changed = !oldDirs.SequenceEqual(newDirs); if (changed) { dirNameToFullPathMap = newMap; OnDirectoriesChanged(); } }
public WurmLogsMonitor([NotNull] IWurmLogFiles wurmLogFiles, [NotNull] IWurmApiLogger logger, [NotNull] IPublicEventInvoker publicEventInvoker, [NotNull] IInternalEventAggregator internalEventAggregator, [NotNull] IWurmCharacterDirectories wurmCharacterDirectories, [NotNull] InternalEventInvoker internalEventInvoker, [NotNull] TaskManager taskManager, [NotNull] LogFileStreamReaderFactory logFileStreamReaderFactory) { if (wurmLogFiles == null) { throw new ArgumentNullException(nameof(wurmLogFiles)); } if (logger == null) { throw new ArgumentNullException(nameof(logger)); } if (publicEventInvoker == null) { throw new ArgumentNullException(nameof(publicEventInvoker)); } if (internalEventAggregator == null) { throw new ArgumentNullException(nameof(internalEventAggregator)); } if (wurmCharacterDirectories == null) { throw new ArgumentNullException(nameof(wurmCharacterDirectories)); } if (internalEventInvoker == null) { throw new ArgumentNullException(nameof(internalEventInvoker)); } if (taskManager == null) { throw new ArgumentNullException(nameof(taskManager)); } if (logFileStreamReaderFactory == null) { throw new ArgumentNullException(nameof(logFileStreamReaderFactory)); } this.wurmLogFiles = wurmLogFiles; this.logger = logger; this.publicEventInvoker = publicEventInvoker; this.internalEventAggregator = internalEventAggregator; this.wurmCharacterDirectories = wurmCharacterDirectories; this.internalEventInvoker = internalEventInvoker; this.taskManager = taskManager; this.logFileStreamReaderFactory = logFileStreamReaderFactory; try { Rebuild(); } catch (Exception exception) { logger.Log(LogLevel.Error, "Error at WurmLogsMonitor initial rebuild", this, exception); } internalEventAggregator.Subscribe(this); taskHandle = new TaskHandle(Rebuild, "WurmLogsMonitor rebuild"); taskManager.Add(taskHandle); updater = new Task(() => { while (true) { if (stop) { return; } Thread.Sleep(500); if (stop) { return; } try { foreach (var logsMonitorEngineManager in characterNameToEngineManagers.Values) { logsMonitorEngineManager.Update(allEventSubscriptionsTsafe); } } catch (Exception exception) { logger.Log(LogLevel.Error, "WurmLogsMonitor 'updater' task crashed", this, exception); } } }, TaskCreationOptions.LongRunning); updater.Start(); taskHandle.Trigger(); }
void EnsureScanned() { if (scanned) { return; } var maxScanSince = Time.Get.LocalNowOffset.AddDays(-30); var lastScanSince = logHistorySaved.LastScanDate.AddDaysSnapToMinMax(-1); var scanSince = lastScanSince < maxScanSince ? maxScanSince : lastScanSince; var allChars = wurmCharacterDirectories.GetAllCharacters(); foreach (var characterName in allChars) { var searchResults = wurmLogsHistory.Scan( new LogSearchParameters() { CharacterName = characterName.Normalized, MinDate = scanSince.DateTime, MaxDate = Time.Get.LocalNow, LogType = LogType.Event }); foreach (var searchResult in searchResults) { var upt = parser.TryParseUptime(searchResult); if (upt != null) { var server = wurmServerHistory.TryGetServer(characterName, searchResult.Timestamp); if (server != null) { logHistorySaved.UpdateHistoric(server, upt); } else { wurmApiLogger.Log(LogLevel.Info, string.Format("Server not found for character {0} at timestamp {1}", characterName, searchResult.Timestamp), this, null); } } var wdt = parser.TryParseWurmDateTime(searchResult); if (wdt != null) { var server = wurmServerHistory.TryGetServer(characterName, searchResult.Timestamp); if (server != null) { logHistorySaved.UpdateHistoric(server, wdt); } else { wurmApiLogger.Log(LogLevel.Info, string.Format("Server not found for character {0} at timestamp {1}", characterName, searchResult.Timestamp), this, null); } } } } scanned = true; }
/// <summary> /// Attempts to parse log entry into a skill gain information. /// Returns null if entry was not recognized as related to skill gains. /// </summary> /// <param name="wurmLogEntry"></param> /// <returns></returns> public SkillInfo TryParseSkillInfoFromLogLine(LogEntry wurmLogEntry) { if (wurmLogEntry.Content.Contains("increased") | wurmLogEntry.Content.Contains("decreased")) { if (wurmLogEntry.Content.EndsWith("affinity", StringComparison.InvariantCulture)) { logger.Log(LogLevel.Info, "Skill message appears to inform about affinity, not supported. Raw entry: " + wurmLogEntry, this, null); return(null); } var match = Regex.Match(wurmLogEntry.Content, @"^(.+) (?:increased|decreased)(.*) to (\d+(?:\,|\.)\d+|\d+).*$", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); string skillName; float? parsedLevel; float? parsedGain = null; if (match.Success) { skillName = match.Groups[1].Value; if (string.IsNullOrWhiteSpace(skillName)) { logger.Log(LogLevel.Error, "Skill name was parsed to empty string, raw entry: " + wurmLogEntry, this, null); return(null); } var rawSkillGain = match.Groups[2].Value; // check if 'by xx.xx' can be present in this entry rawSkillGain = rawSkillGain.Trim(); if (rawSkillGain.Length > 3 && rawSkillGain.StartsWith("by ")) { //try parse the skill gain parsedGain = TryParseFloatInvariant(rawSkillGain.Remove(0, 3).Trim()); if (parsedGain == null) { logger.Log(LogLevel.Error, "Skill gain appears to be in log entry content, but could not be parsed, raw string: " + rawSkillGain + " raw entry: " + wurmLogEntry, this, null); } } var rawSkillLevel = match.Groups[3].Value; parsedLevel = TryParseFloatInvariant(rawSkillLevel); if (parsedLevel == null) { logger.Log(LogLevel.Error, "Skill level could not be parsed, raw value: " + rawSkillLevel + " raw entry: " + wurmLogEntry, this, null); return(null); } } else { logger.Log(LogLevel.Error, "Skill gain/loss message could not be parsed, raw entry: " + wurmLogEntry, this, null); return(null); } return(new SkillInfo(skillName, parsedLevel.Value, wurmLogEntry.Timestamp, parsedGain)); } else { return(null); } }