Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using Newtonsoft.Json;
- using Newtonsoft.Json.Linq;
- class Solution {
- /// <summary>
- /// Types of messages we want to track
- /// </summary>
- public static readonly string[] TargetedMessages = { "Start Session", "Start Query", "End Query", "End Session", "Session started.", "Session ended.", "Query: Start", "Query: End" };
- static void Main(string[] args)
- {
- var sessionCollection = new Dictionary<Guid, SessionInfo>();
- // Loop through rows
- string row = string.Empty;
- do
- {
- try
- {
- row = Console.ReadLine();
- if (string.IsNullOrEmpty(row)) continue;
- LogEntry entry;
- if (row.StartsWith("{") && row.EndsWith("}"))
- {
- try
- {
- entry = JsonConvert.DeserializeObject<LogEntry>(row);
- }
- catch { continue; } // Continue if deserialization failed
- }
- else
- {
- if (row.Length < 66) continue; // rows should be at least 66 characters long. If not, continue
- if (!DateTime.TryParse(row.Substring(0, 25), out var timeStamp)) continue; // Continue if timestamp parsing failed
- if (!Guid.TryParse(row.Substring(26, 36), out var sessionId)) continue; // Continue if guid parsing failed
- var durationEndIndex = row.IndexOf(' ', 63);
- if (durationEndIndex == -1) continue; // Continue if misformatted row
- if (!long.TryParse(row.Substring(63, durationEndIndex - 63), out var duration)) continue; // Continue if duration parsing failed
- if (durationEndIndex + 1 >= row.Length) continue; // Continue if empty message
- var message = row.Substring(durationEndIndex + 1);
- entry = new LogEntry(timeStamp, sessionId, duration, message);
- }
- if (entry == null
- || entry.Timestamp == null
- || string.IsNullOrEmpty(entry.Message)
- || !entry.Session.HasValue
- || entry.Session.Value == Guid.Empty) continue; // Continue if the entry is missing information
- if (sessionCollection.TryGetValue(entry.Session.Value, out var session))
- {
- // If session is already in the collection, add a new LogEntry to it
- session.AddEntry(entry);
- }
- else
- {
- // If session was not found, let's add one
- sessionCollection.Add(entry.Session.Value, new SessionInfo(entry));
- }
- }
- catch { }
- } while (!string.IsNullOrEmpty(row));
- // Extract only the values from our dictionary, order them and serialize
- var resultString = JsonConvert.SerializeObject(sessionCollection.Values
- .OrderBy(x => x.StartTimestampUtc)
- .ThenBy(x => x.TotalDuration)
- .ToList(),
- Formatting.None,
- new JsonSerializerSettings() {
- DateTimeZoneHandling = DateTimeZoneHandling.Utc
- });
- // Format the result
- var formatted = JValue.Parse(resultString).ToString(Formatting.Indented);
- Console.Write(formatted);
- }
- /// <summary>
- /// Class that stores session information.
- /// </summary>
- public class SessionInfo
- {
- [JsonProperty(PropertyName = "startTimestampUtc")]
- public DateTime? StartTimestampUtc { get; set; }
- [JsonProperty(PropertyName = "endTimestampUtc")]
- public DateTime? EndTimestampUtc { get; set; }
- [JsonProperty(PropertyName = "sessionId"), JsonConverter(typeof(GuidToStringConverter))]
- public Guid SessionId { get; set; }
- [JsonProperty(PropertyName = "totalDuration")]
- public long TotalDuration { get; set; }
- [JsonProperty(PropertyName = "queryCount")]
- public long QueryCount
- {
- get
- {
- int count = 0, endIdx = 0;
- var startedQueries = LogEntries.Where(x => x.Message == ("Query: Start") || x.Message == "Start Query").OrderBy(x => x.Timestamp).Select(x => x.Timestamp).ToArray();
- var endedQueries = LogEntries.Where(x => x.Message == ("Query: End") || x.Message == "End Query").OrderBy(x => x.Timestamp).Select(x => x.Timestamp).ToArray();
- for(var i = 0; i < startedQueries.Length; i++)
- {
- for(var e = endIdx; e < endedQueries.Length; e++)
- {
- if (endedQueries[e] >= startedQueries[i])
- {
- count++;
- endIdx = e + 1;
- break;
- }
- if (count == endedQueries.Length || endIdx == endedQueries.Length) return count;
- }
- }
- return count;
- }
- }
- [JsonIgnore]
- public List<LogEntry> LogEntries { get; set; }
- /// <summary>
- /// Constructor that takes in the initial LogEntry and builds the initial SessionInfo object.
- /// </summary>
- /// <param name="entry">The entry to process</param>
- public SessionInfo(LogEntry entry)
- {
- SessionId = entry.Session ?? Guid.Empty;
- TotalDuration = entry.Duration ?? 0;
- RecordStartEndTimestamp(entry);
- LogEntries = new List<LogEntry>() { entry };
- }
- /// <summary>
- /// Method for adding a LogEntry to a SessionInfo object.
- /// </summary>
- /// <param name="entry">The entry to process</param>
- public void AddEntry(LogEntry entry)
- {
- RecordStartEndTimestamp(entry);
- TotalDuration += entry.Duration ?? 0;
- LogEntries.Add(entry);
- }
- /// <summary>
- /// Private method that evaluates the log message and makes changes to the start/end timetamps when required.
- /// </summary>
- /// <param name="entry">The entry to process</param>
- private void RecordStartEndTimestamp(LogEntry entry)
- {
- if (entry.Message == "Session started." || entry.Message == "Start Session")
- {
- StartTimestampUtc = entry.Timestamp;
- }
- else if (entry.Message == "Session ended." || entry.Message == "End Session")
- {
- EndTimestampUtc = entry.Timestamp;
- }
- }
- }
- /// <summary>
- /// Class that holds information about individual log entries
- /// </summary>
- public class LogEntry
- {
- [JsonProperty(PropertyName = "t")]
- public DateTime? Timestamp { get; set; }
- [JsonProperty(PropertyName = "s")]
- public Guid? Session { get; set; }
- [JsonProperty(PropertyName = "d")]
- public long? Duration { get; set; }
- [JsonProperty(PropertyName = "m")]
- public string Message { get; set; }
- [JsonProperty(PropertyName = "r")]
- public long? ResultCount { get; set; }
- [JsonProperty(PropertyName = "cache-hit")]
- public bool? CacheHit { get; set; }
- public LogEntry() { }
- /// <summary>
- /// LogEntry constructor that parses the log message for cache hits and results
- /// </summary>
- /// <param name="timeStamp">The timestamp</param>
- /// <param name="session">Session Id Guid</param>
- /// <param name="duration">Duration in ms</param>
- /// <param name="message">The message</param>
- public LogEntry(DateTime timeStamp, Guid session, long duration, string message)
- {
- Timestamp = timeStamp;
- Session = session;
- Duration = duration;
- if (message.StartsWith("Start Query (cache-hit:"))
- {
- if (bool.TryParse(message.Substring(23, message.Length - 24), out var cache))
- CacheHit = cache;
- else
- {
- Session = null;
- }
- Message = "Start Query";
- }
- else if (message.StartsWith("End Query (results:"))
- {
- if (long.TryParse(message.Substring(20, message.Length - 21), out var results))
- ResultCount = results;
- else
- {
- Session = null;
- }
- Message = "End Query";
- }
- else Message = message;
- }
- }
- /// <summary>
- /// Custom Json converter for Guids
- /// </summary>
- public class GuidToStringConverter : JsonConverter
- {
- public override bool CanConvert(Type objectType)
- {
- return true;
- }
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
- {
- writer.WriteValue(((Guid)value).ToString("N"));
- }
- public override bool CanRead { get { return false; } }
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
- {
- throw new NotImplementedException();
- }
- }
- }
Add Comment
Please, Sign In to add comment