-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Updated DRES results logging to include stage index for staged queries. * Fixed bug logging object as segment query results. * Refactored results logging from DresClientManager to a new logging class separating file and DRES logging. * Refactored submission logging and fixed fast file logging through semaphores. * Refactored interaction logging. Moved interaction logging functions to LoggingController. Removed all file logging from DresClientManager. * Implemented more fine grained custom interaction logging category enum. * Implemented logging to file of source calling class.
- Loading branch information
Showing
15 changed files
with
446 additions
and
217 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
namespace VitrivrVR.Logging | ||
{ | ||
public enum Interaction | ||
{ | ||
TextInput, | ||
QueryFormulation, | ||
Query, | ||
QueryManagement, | ||
Browsing, | ||
Filter, | ||
ResultExpansion, | ||
Other | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,259 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.Globalization; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Threading; | ||
using Newtonsoft.Json; | ||
using Org.Vitrivr.CineastApi.Model; | ||
using Vitrivr.UnityInterface.CineastApi.Model.Data; | ||
using VitrivrVR.Config; | ||
using VitrivrVR.Notification; | ||
using VitrivrVR.Submission; | ||
|
||
namespace VitrivrVR.Logging | ||
{ | ||
/// <summary> | ||
/// Static class for all interaction and usage logs. | ||
/// | ||
/// The goal of this class is to separate the log sources from different kinds of loggers, such as file and | ||
/// competition loggers. | ||
/// </summary> | ||
public static class LoggingController | ||
{ | ||
private static readonly string StartDate = DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss"); | ||
private static readonly string LogDir = Path.Combine(ConfigManager.Config.logFileLocation, StartDate); | ||
private static readonly string ResultLogLocation = Path.Combine(LogDir, "results.txt"); | ||
private static readonly string SubmissionLogLocation = Path.Combine(LogDir, "submission.txt"); | ||
private static readonly string InteractionLogLocation = Path.Combine(LogDir, "interactions.txt"); | ||
|
||
private static readonly SemaphoreSlim SubmissionLogLock = new(1, 1); | ||
private static readonly SemaphoreSlim ResultLogLock = new(1, 1); | ||
private static readonly SemaphoreSlim InteractionLogLock = new(1, 1); | ||
|
||
private static long CurrentTimestamp => DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); | ||
|
||
/// <summary> | ||
/// Logs ranked results lists for similarity and staged similarity queries. | ||
/// </summary> | ||
/// <param name="sortOrder">The order in which the ranked results are displayed (e.g. by segment or object).</param> | ||
/// <param name="results">List of ranked results.</param> | ||
/// <param name="queryResponse">Query response containing the source query.</param> | ||
public static void LogQueryResults(string sortOrder, List<ScoredSegment> results, QueryResponse queryResponse) | ||
{ | ||
var timestamp = CurrentTimestamp; | ||
|
||
// Log to DRES | ||
if (ConfigManager.Config.dresEnabled) | ||
{ | ||
// Similarity query | ||
if (queryResponse.Query != null) | ||
{ | ||
DresClientManager.LogResults(timestamp, sortOrder, results, queryResponse.Query); | ||
} | ||
|
||
// Staged query | ||
if (queryResponse.StagedQuery != null) | ||
{ | ||
DresClientManager.LogResults(timestamp, sortOrder, results, queryResponse.StagedQuery); | ||
} | ||
} | ||
|
||
// Log to file | ||
if (ConfigManager.Config.writeLogsToFile) | ||
{ | ||
LogQueryResultsToFile(timestamp, sortOrder, results, queryResponse); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Logs ranked results lists for temporal similarity queries. | ||
/// </summary> | ||
public static void LogQueryResults(string sortOrder, List<TemporalObject> results, | ||
TemporalQueryResponse queryResponse) | ||
{ | ||
var timestamp = CurrentTimestamp; | ||
|
||
// Log to DRES | ||
if (ConfigManager.Config.dresEnabled) | ||
{ | ||
DresClientManager.LogResults(timestamp, sortOrder, results, queryResponse.Query); | ||
} | ||
|
||
// Log to file | ||
if (ConfigManager.Config.writeLogsToFile) | ||
{ | ||
LogQueryResultsToFile(timestamp, sortOrder, results, queryResponse); | ||
} | ||
} | ||
|
||
public static void LogSubmission(string mediaObjectId, int? frame) | ||
{ | ||
var timestamp = CurrentTimestamp; | ||
// Log to file | ||
if (ConfigManager.Config.writeLogsToFile) | ||
{ | ||
LogSubmissionToFile(timestamp, mediaObjectId, frame); | ||
} | ||
} | ||
|
||
public static void LogInteraction(string type, string value, Interaction category) | ||
{ | ||
var timestamp = CurrentTimestamp; | ||
var source = new StackTrace().GetFrame(1).GetMethod().ReflectedType?.Name; | ||
|
||
// Log to DRES | ||
if (ConfigManager.Config.dresEnabled) | ||
{ | ||
DresClientManager.LogInteraction(timestamp, type, value, category); | ||
} | ||
|
||
// Log to file | ||
if (ConfigManager.Config.writeLogsToFile) | ||
{ | ||
LogInteractionToFile(timestamp, source, type, value, category.ToString()); | ||
} | ||
} | ||
|
||
#region FileLogger | ||
|
||
private static void EnsureDirectoryExists() | ||
{ | ||
Directory.CreateDirectory(LogDir); | ||
} | ||
|
||
private static async void LogQueryResultsToFile(long timestamp, string sortOrder, | ||
IEnumerable<ScoredSegment> results, | ||
QueryResponse queryResponse) | ||
{ | ||
EnsureDirectoryExists(); | ||
await ResultLogLock.WaitAsync(); | ||
try | ||
{ | ||
await using var file = new StreamWriter(ResultLogLocation, true); | ||
var serializableResults = results.Select(segment => new Dictionary<string, string> | ||
{ | ||
{ "id", segment.segment.Id }, | ||
{ "score", segment.score.ToString(CultureInfo.InvariantCulture) } | ||
}); | ||
var jsonResults = JsonConvert.SerializeObject(serializableResults, Formatting.None); | ||
var jsonQuery = QueryToJson(queryResponse); | ||
|
||
var resultLog = | ||
$"{{\"timestamp\":{timestamp},\"sortOrder\":\"{sortOrder}\",\"query\":{jsonQuery},\"results\":{jsonResults}}}"; | ||
await file.WriteLineAsync(resultLog); | ||
} | ||
catch (Exception e) | ||
{ | ||
NotificationController.NotifyError($"Error logging to file: {e.Message}", e); | ||
} | ||
finally | ||
{ | ||
ResultLogLock.Release(); | ||
} | ||
} | ||
|
||
private static async void LogQueryResultsToFile(long timestamp, string sortOrder, List<TemporalObject> results, | ||
TemporalQueryResponse queryResponse) | ||
{ | ||
EnsureDirectoryExists(); | ||
await ResultLogLock.WaitAsync(); | ||
try | ||
{ | ||
await using var file = new StreamWriter(ResultLogLocation, true); | ||
var jsonResults = JsonConvert.SerializeObject(results, Formatting.None); | ||
var jsonQuery = JsonConvert.SerializeObject(queryResponse.Query, Formatting.None); | ||
|
||
var resultLog = | ||
$"{{\"timestamp\":{timestamp},\"sortOrder\":\"{sortOrder}\",\"query\":{jsonQuery},\"results\":{jsonResults}}}"; | ||
await file.WriteLineAsync(resultLog); | ||
} | ||
catch (Exception e) | ||
{ | ||
NotificationController.NotifyError($"Error logging to file: {e.Message}", e); | ||
} | ||
finally | ||
{ | ||
ResultLogLock.Release(); | ||
} | ||
} | ||
|
||
private static string QueryToJson(QueryResponse queryResponse) | ||
{ | ||
if (queryResponse.Query != null) | ||
{ | ||
return JsonConvert.SerializeObject(queryResponse.Query, Formatting.None); | ||
} | ||
|
||
if (queryResponse.StagedQuery != null) | ||
{ | ||
return JsonConvert.SerializeObject(queryResponse.StagedQuery, Formatting.None); | ||
} | ||
|
||
throw new Exception("Query response contains neither similarity nor staged similarity query."); | ||
} | ||
|
||
private static async void LogSubmissionToFile(long timestamp, string mediaObjectId, int? frame) | ||
{ | ||
EnsureDirectoryExists(); | ||
await SubmissionLogLock.WaitAsync(); | ||
try | ||
{ | ||
await using var file = new StreamWriter(SubmissionLogLocation, true); | ||
var dict = new Dictionary<string, string> | ||
{ | ||
{ "timestamp", timestamp.ToString() }, | ||
{ "mediaObjectId", mediaObjectId } | ||
}; | ||
if (frame.HasValue) | ||
dict["frame"] = frame.ToString(); | ||
|
||
var json = JsonConvert.SerializeObject(dict, Formatting.None); | ||
|
||
await file.WriteLineAsync(json); | ||
} | ||
catch (Exception e) | ||
{ | ||
NotificationController.NotifyError($"Error logging to file: {e.Message}"); | ||
} | ||
finally | ||
{ | ||
SubmissionLogLock.Release(); | ||
} | ||
} | ||
|
||
private static async void LogInteractionToFile(long timestamp, string source, string type, string value, | ||
string category) | ||
{ | ||
EnsureDirectoryExists(); | ||
await InteractionLogLock.WaitAsync(); | ||
try | ||
{ | ||
await using var file = new StreamWriter(InteractionLogLocation, true); | ||
var dict = new Dictionary<string, string> | ||
{ | ||
{ "timestamp", timestamp.ToString() }, | ||
{ "source", source }, | ||
{ "type", type }, | ||
{ "value", value }, | ||
{ "category", category } | ||
}; | ||
|
||
var json = JsonConvert.SerializeObject(dict, Formatting.None); | ||
|
||
await file.WriteLineAsync(json); | ||
} | ||
catch (Exception e) | ||
{ | ||
NotificationController.NotifyError($"Error logging to file: {e.Message}"); | ||
} | ||
finally | ||
{ | ||
InteractionLogLock.Release(); | ||
} | ||
} | ||
|
||
#endregion | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.