/* Copyright (c) Microsoft Corporation All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. */ namespace Microsoft.Research.Calypso.JobObjectModel { using System.Text.RegularExpressions; using System; using System.Diagnostics; /// /// Information about a standard Cosmos log entry. /// [Serializable] public class CosmosLogEntry : IParse { /// /// Message severity. /// public char Severity { get; protected set; } /// /// Timestamp. /// public DateTime Timestamp { get; protected set; } /// /// Subsystem logging the message. /// public string Subsystem { get; protected set; } /// /// Unique message string. /// public string Message { get; protected set; } /// /// Source file containing the code logging the message. /// public string SourceFile { get; protected set; } /// /// Source code function logging the message. /// public string SourceFunction { get; protected set; } /// /// Line number in source file of the logging statement. /// public int SourceLineNumber { get; protected set; } /// /// ID of process logging the message. /// public int ProcessId { get; protected set; } /// /// Id of thread logging the message. /// public int ThreadId { get; protected set; } /// /// Extra unformatted information about the message. /// public string ExtraInfo { get; protected set; } /// /// If true the message could not be parsed. /// public bool Malformed { get; protected set; } /// /// The line which produced by parsing this message. /// public string OriginalLogLine { get; protected set; } static Regex loglineregex = new Regex(@"(\w+), # 1 severity ([^,]+), # 2 timestamp (\w*), # 3 level (.*), # 4 message, free form! SrcFile=""(\S+)""\s+ # 5 source file SrcFunc=""(\S+)""\s+ # 6 source function SrcLine=""(\d+)""\s+ # 7 line Pid=""(\d+)""\s+ # 8 process Tid=""(\d+)""\s+ # 9 thread TS=""(\w+)""\s* # 10 ? (String1=""(.*)"")?$ # 11,12 additional arguments ", RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace); /// /// Allocate an empty log entry /// public CosmosLogEntry() { this.Malformed = true; } /// /// Allocate a log entry from a given string. /// /// String to initialize the log entry. public CosmosLogEntry(string line) { this.Malformed = true; // ReSharper disable once DoNotCallOverridableMethodsInConstructor this.Parse(line); } /// /// Return 'true' if the log entry signals an error. /// public bool IsError { get { return !this.Malformed && (this.Severity == 'a' || this.Severity == 'e'); } } /// /// Convert a log entry to a string (returns the original source line). /// /// The original log line that was parsed. public override string ToString() { return this.OriginalLogLine; } /// /// Parse a log entry line. /// /// Dryad log line to be parsed. public virtual void Parse(string line) // i,09/09/2008 10:41:43.829,Cosmos,Found cluster alias,SrcFile="csnameresolver.cpp" SrcFunc="CsNameResolver::BuildClusterAliasMap" SrcLine="537" Pid="1936" Tid="1552" TS="0x01C912A35300C167" String1="AliasCluster=codev2 RealCluster=cosmos02-dev-co1" { this.OriginalLogLine = line; Match m = loglineregex.Match(line); if (m.Success) { this.Severity = m.Groups[1].Value.ToCharArray()[0]; this.Timestamp = DateTime.Parse(m.Groups[2].Value); this.Subsystem = m.Groups[3].Value; this.Message = m.Groups[4].Value; this.SourceFile = m.Groups[5].Value; this.SourceFunction = m.Groups[6].Value; this.SourceLineNumber = Int32.Parse(m.Groups[7].Value); this.ProcessId = Int32.Parse(m.Groups[8].Value); this.ThreadId = Int32.Parse(m.Groups[9].Value); if (m.Groups.Count >= 12) // the String1 may be missing this.ExtraInfo = m.Groups[12].Value; else this.ExtraInfo = ""; this.Malformed = false; } else { Trace.TraceInformation("Could not parse line as cosmos log entry:" + line); } } } /// /// An extended log entry is like a cosmos log entry, but it has a prefix: GUID,Machine /// [Serializable] public class ExtendedLogEntry : CosmosLogEntry { static Regex loglineregex = new Regex(@"([-0-9A-F]+), # 1 guid, inserted by reader (\S+), # 2 machine (.), # 3 severity ([^,]+), # 4 timestamp (\w+), # 5 level (.+), # 6 message, free form! SrcFile=""(\S+)""\s+ # 7 source file SrcFunc=""(\S+)""\s+ # 8 source function SrcLine=""(\d+)""\s+ # 9 line Pid=""(\d+)""\s+ # 10 process Tid=""(\d+)""\s+ # 11 thread TS=""(\w+)""\s* # 12 ? (String1=""(.*)"")?$ # 14 additional arguments ", RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace); /// /// When parsing fails, use this simple parser. /// static Regex simpleloglineregex = new Regex(@"([-0-9A-F]+), # 1 guid (\w+), # 2 machine (.*) # 3 rest of LineRecord", RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace); /// /// The guid of the process that logged this entry. /// public string Guid { get; protected set; } /// /// The machine where the log entry resides. /// public string Machine { get; protected set; } /// /// Parse an extended log entry line. /// /// Dryad log line to be parsed. public override void Parse(string line) // i,09/09/2008 10:41:43.829,Cosmos,Found cluster alias,SrcFile="csnameresolver.cpp" SrcFunc="CsNameResolver::BuildClusterAliasMap" SrcLine="537" Pid="1936" Tid="1552" TS="0x01C912A35300C167" String1="AliasCluster=codev2 RealCluster=cosmos02-dev-co1" { this.OriginalLogLine = line; Match m = loglineregex.Match(line); if (m.Success) { try { this.Guid = m.Groups[1].Value; this.Machine = m.Groups[2].Value; this.Severity = m.Groups[3].Value.ToCharArray()[0]; this.Timestamp = DateTime.Parse(m.Groups[4].Value); this.Subsystem = m.Groups[5].Value; this.Message = m.Groups[6].Value; this.SourceFile = m.Groups[7].Value; this.SourceFunction = m.Groups[8].Value; this.SourceLineNumber = Int32.Parse(m.Groups[9].Value); this.ProcessId = Int32.Parse(m.Groups[10].Value); this.ThreadId = Int32.Parse(m.Groups[11].Value); if (m.Groups.Count >= 14) // the String1 may be missing this.ExtraInfo = m.Groups[14].Value; else this.ExtraInfo = ""; this.Malformed = false; } catch (FormatException) { Trace.TraceInformation("Failed to parse as extended log entry: {0}", line); this.Malformed = true; } } else this.Malformed = true; if (this.Malformed) // much simpler parsing { m = simpleloglineregex.Match(line); if (m.Success) { this.Guid = m.Groups[1].Value; this.Machine = m.Groups[2].Value; } else { this.Guid = "?"; this.Machine = "?"; } this.Severity = '?'; this.Timestamp = DateTime.Now; this.Subsystem = ""; this.Message = ""; this.SourceFile = ""; this.SourceFunction = ""; this.SourceLineNumber = 0; this.ProcessId = 0; this.ThreadId = 0; this.ExtraInfo = ""; } } } /// /// Log entries have changed when switching to Argentia. /// [Serializable] public class ArgentiaDryadLogEntry : IParse { static Regex loglineregex = new Regex(@"(\w), # 1 severity (.*), # 2 source function (\S+): # 3 source file (\d+), # 4 line number (\S+) # 5 message ", RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace); /// /// Message severity. /// public char Severity { get; protected set; } /// /// Source file containing the code logging the message. /// public string SourceFile { get; protected set; } /// /// Source code function logging the message. /// public string SourceFunction { get; protected set; } /// /// If true the message could not be parsed. /// public bool Malformed { get; protected set; } /// /// The line which produced by parsing this message. /// public string OriginalLogLine { get; protected set; } /// /// Line number in source file of the logging statement. /// public int SourceLineNumber { get; protected set; } /// /// Unique message string. /// public string Message { get; protected set; } /// /// Parse a line as a log message. /// /// Line to parse. public void Parse(string line) { this.OriginalLogLine = line; Match m = loglineregex.Match(line); if (m.Success) { this.Severity = m.Groups[1].Value.ToCharArray()[0]; this.SourceFunction = m.Groups[2].Value; this.SourceFile = m.Groups[3].Value; this.SourceLineNumber = Int32.Parse(m.Groups[4].Value); this.Message = m.Groups[5].Value; this.Malformed = false; } else { this.Malformed = true; } } } }