Skip to content

Commit ddb7591

Browse files
author
Lessley Dennington
committed
trace2: write version and start events
Write the TRACE2 version event, which identifies the event format version (currently hardcoded as 3 as it is in Git) and the version of GCM. Additionally, write the TRACE2 start event, which reflects the elapsed time and application arguments (including the application name). These are paired because the telemetry tool/OTel collector require both these events to be sent for a given session.
1 parent 8dca18b commit ddb7591

File tree

2 files changed

+145
-6
lines changed

2 files changed

+145
-6
lines changed

src/shared/Core/Trace2.cs

Lines changed: 138 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.IO;
55
using System.IO.Pipes;
66
using System.Linq;
7+
using System.Runtime.Serialization;
78
using System.Text;
89
using Newtonsoft.Json;
910
using Newtonsoft.Json.Converters;
@@ -16,7 +17,12 @@ namespace GitCredentialManager;
1617
/// system.
1718
/// </summary>
1819
public enum Trace2Event
19-
{ }
20+
{
21+
[EnumMember(Value = "version")]
22+
Version = 0,
23+
[EnumMember(Value = "start")]
24+
Start = 1,
25+
}
2026

2127
public class Trace2Settings
2228
{
@@ -36,7 +42,13 @@ public interface ITrace2 : IDisposable
3642
/// <param name="error">The standard error text stream connected back to the calling process.</param>
3743
/// <param name="fileSystem">File system abstraction.</param>
3844
/// <param name="appPath">The path to the GCM application.</param>
39-
void Start(TextWriter error, IFileSystem fileSystem, string appPath);
45+
/// <param name="filePath">Path of the file this method is called from.</param>
46+
/// <param name="lineNumber">Line number of file this method is called from.</param>
47+
void Start(TextWriter error,
48+
IFileSystem fileSystem,
49+
string appPath,
50+
[System.Runtime.CompilerServices.CallerFilePath] string filePath = "",
51+
[System.Runtime.CompilerServices.CallerLineNumber] int lineNumber = 0);
4052
}
4153

4254
public class Trace2 : DisposableObject, ITrace2
@@ -63,9 +75,22 @@ public Trace2(IEnvironment environment, Trace2Settings settings, string[] argv,
6375
_sid = SetSid();
6476
}
6577

66-
public void Start(TextWriter error, IFileSystem fileSystem, string appPath)
78+
public void Start(TextWriter error,
79+
IFileSystem fileSystem,
80+
string appPath,
81+
string filePath,
82+
int lineNumber)
6783
{
6884
TryParseSettings(error, fileSystem);
85+
86+
if (!AssemblyUtils.TryGetAssemblyVersion(out string version))
87+
{
88+
// A version is required for TRACE2, so if this call fails
89+
// manually set the version.
90+
version = "0.0.0";
91+
}
92+
WriteVersion(version, filePath, lineNumber);
93+
WriteStart(appPath, filePath, lineNumber);
6994
}
7095

7196
protected override void ReleaseManagedResources()
@@ -164,6 +189,50 @@ private void TryParseSettings(TextWriter error, IFileSystem fileSystem)
164189
}
165190
}
166191

192+
private void WriteVersion(
193+
string gcmVersion,
194+
string filePath,
195+
int lineNumber,
196+
string eventFormatVersion = "3")
197+
{
198+
EnsureArgument.NotNull(gcmVersion, nameof(gcmVersion));
199+
200+
WriteMessage(new VersionMessage()
201+
{
202+
Event = Trace2Event.Version,
203+
Sid = _sid,
204+
Time = DateTimeOffset.UtcNow,
205+
File = Path.GetFileName(filePath).ToLower(),
206+
Line = lineNumber,
207+
Evt = eventFormatVersion,
208+
Exe = gcmVersion
209+
});
210+
}
211+
212+
private void WriteStart(
213+
string appPath,
214+
string filePath,
215+
int lineNumber)
216+
{
217+
// Prepend GCM exe to arguments
218+
var argv = new List<string>()
219+
{
220+
Path.GetFileName(appPath),
221+
};
222+
argv.AddRange(_argv);
223+
224+
WriteMessage(new StartMessage()
225+
{
226+
Event = Trace2Event.Start,
227+
Sid = _sid,
228+
Time = DateTimeOffset.UtcNow,
229+
File = Path.GetFileName(filePath).ToLower(),
230+
Line = lineNumber,
231+
Argv = argv,
232+
ElapsedTime = (DateTimeOffset.UtcNow - _applicationStartTime).TotalSeconds
233+
});
234+
}
235+
167236
private void AddWriter(ITrace2Writer writer)
168237
{
169238
ThrowIfDisposed();
@@ -203,6 +272,7 @@ private void WriteMessage(Trace2Message message)
203272
public abstract class Trace2Message
204273
{
205274
protected const string TimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'ffffff'Z'";
275+
private const int SourceColumnMaxWidth = 23;
206276

207277
[JsonProperty("event", Order = 1)]
208278
public Trace2Event Event { get; set; }
@@ -226,4 +296,69 @@ public abstract class Trace2Message
226296
public abstract string ToJson();
227297

228298
public abstract string ToNormalString();
299+
300+
protected string BuildNormalString(string message)
301+
{
302+
// The normal format uses local time rather than UTC time.
303+
string time = Time.ToLocalTime().ToString("HH:mm:ss.ffffff");
304+
305+
// Source column format is file:line
306+
string source = $"{File.ToLower()}:{Line}";
307+
if (source.Length > SourceColumnMaxWidth)
308+
{
309+
source = TraceUtils.FormatSource(source, SourceColumnMaxWidth);
310+
}
311+
312+
// Git's TRACE2 normal format is:
313+
// [<time> SP <filename>:<line> SP+] <event-name> [[SP] <event-message>] LF
314+
return $"{time} {source,-33} {Event.ToString().ToLower()} {message}";
315+
}
316+
}
317+
318+
public class VersionMessage : Trace2Message
319+
{
320+
[JsonProperty("evt", Order = 7)]
321+
public string Evt { get; set; }
322+
323+
[JsonProperty("exe", Order = 8)]
324+
public string Exe { get; set; }
325+
326+
public override string ToJson()
327+
{
328+
return JsonConvert.SerializeObject(this,
329+
new StringEnumConverter(),
330+
new IsoDateTimeConverter()
331+
{
332+
DateTimeFormat = TimeFormat
333+
});
334+
}
335+
336+
public override string ToNormalString()
337+
{
338+
return BuildNormalString(Exe.ToLower());
339+
}
340+
}
341+
342+
public class StartMessage : Trace2Message
343+
{
344+
[JsonProperty("t_abs", Order = 7)]
345+
public double ElapsedTime { get; set; }
346+
347+
[JsonProperty("argv", Order = 8)]
348+
public List<string> Argv { get; set; }
349+
350+
public override string ToJson()
351+
{
352+
return JsonConvert.SerializeObject(this,
353+
new StringEnumConverter(),
354+
new IsoDateTimeConverter()
355+
{
356+
DateTimeFormat = TimeFormat
357+
});
358+
}
359+
360+
public override string ToNormalString()
361+
{
362+
return BuildNormalString(string.Join(" ", Argv));
363+
}
229364
}

src/shared/TestInfrastructure/Objects/NullTrace.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,17 @@ void IDisposable.Dispose() { }
4747

4848
#endregion
4949
}
50-
50+
5151
public class NullTrace2 : ITrace2
5252
{
5353
#region ITrace2
5454
public void AddWriter(ITrace2Writer writer) { }
55-
56-
public void Start(TextWriter error, IFileSystem fileSystem, string appPath) { }
55+
56+
public void Start(TextWriter error,
57+
IFileSystem fileSystem,
58+
string appPath,
59+
string filePath = "",
60+
int lineNumber = 0) { }
5761

5862
#endregion
5963

0 commit comments

Comments
 (0)