diff --git a/src/Directory.Build.props b/src/Directory.Build.props index bb229e0..d1fbc72 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,6 +1,6 @@ - 0.4.1 + 0.4.2 Tony Redondo, Grégory Léocadie net6.0;net7.0;net8.0;net9.0 enable diff --git a/src/TimeItSharp.Common/ScenarioProcessor.cs b/src/TimeItSharp.Common/ScenarioProcessor.cs index b646c6d..fa6bdec 100644 --- a/src/TimeItSharp.Common/ScenarioProcessor.cs +++ b/src/TimeItSharp.Common/ScenarioProcessor.cs @@ -4,11 +4,8 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using System.Text; -using System.Text.Json; -using System.Text.Json.Nodes; using CliWrap; using CliWrap.Buffered; -using DatadogTestLogger.Vendors.Datadog.Trace; using MathNet.Numerics.Distributions; using MathNet.Numerics.Statistics; using Spectre.Console; @@ -16,6 +13,7 @@ using TimeItSharp.Common.Configuration; using TimeItSharp.Common.Results; using TimeItSharp.Common.Services; +using TimeItSharp.RuntimeMetrics; using Status = TimeItSharp.Common.Results.Status; namespace TimeItSharp.Common; @@ -794,21 +792,29 @@ private async Task RunCommandAsync(int index, Scenario scenario, Time var metrics = new Dictionary(); var metricsCount = new Dictionary(); - Span bytesBuffer = stackalloc byte[1024]; - foreach (var metricJsonItem in File.ReadLines(metricsFilePath)) + await using (var file = File.Open(metricsFilePath, FileMode.Open, FileAccess.Read, FileShare.Read)) + using (var reader = new BinaryReader(file)) { - try + while (file.Position < file.Length) { - var bytesCount = Encoding.UTF8.GetBytes(metricJsonItem, bytesBuffer); - var metricsJsonItemBytes = bytesBuffer.Slice(0, bytesCount); - var jsonReader = new Utf8JsonReader(metricsJsonItemBytes); - if (JsonDocument.TryParseValue(ref jsonReader, out var jsonMetricsItem)) + // Read magic number + if (reader.ReadInt32() != 7248) { - var jsonRoot = jsonMetricsItem.RootElement; - var type = jsonRoot.GetProperty("type").GetString(); - var name = jsonRoot.GetProperty("name").GetString(); - var value = jsonRoot.GetProperty("value").GetDouble(); + continue; + } + // Read metric type + var type = (BinaryFileStorage.MetricType)reader.ReadByte(); + // Read name length + var nameLength = reader.ReadInt32(); + // Read name + var nameBytes = reader.ReadBytes(nameLength); + var name = Encoding.UTF8.GetString(nameBytes); + // Read value + var value = reader.ReadDouble(); + + try + { if (name is not null) { static void EnsureMainDuration(Dictionary values, @@ -816,7 +822,7 @@ static void EnsureMainDuration(Dictionary values, { if (mainStartDate is not null && mainEndDate is not null) { - values[Constants.ProcessInternalDurationMetricName] = + values[Constants.ProcessInternalDurationMetricNameString] = (mainEndDate.Value - mainStartDate.Value).TotalMilliseconds; } } @@ -838,26 +844,26 @@ mainEndDate is not null && var internalDuration = (endDate.Value - startDate.Value).TotalMilliseconds; var overheadDuration = internalDuration - mainDuration; var globalDuration = (point.End - point.Start).TotalMilliseconds; - values[Constants.ProcessStartupHookOverheadMetricName] = overheadDuration; - values[Constants.ProcessCorrectedDurationMetricName] = + values[Constants.ProcessStartupHookOverheadMetricNameString] = overheadDuration; + values[Constants.ProcessCorrectedDurationMetricNameString] = globalDuration - overheadDuration; } } - if (name == Constants.ProcessStartTimeUtcMetricName) + if (name == Constants.ProcessStartTimeUtcMetricNameString) { inProcStartDate = DateTime.FromBinary((long)value); - metrics[Constants.ProcessTimeToStartMetricName] = + metrics[Constants.ProcessTimeToStartMetricNameString] = (inProcStartDate.Value - dataPoint.Start).TotalMilliseconds; EnsureStartupHookOverhead(dataPoint, metrics, inProcStartDate, inProcMainStartDate, inProcMainEndDate, inProcEndDate); continue; } - if (name == Constants.MainMethodStartTimeUtcMetricName) + if (name == Constants.MainMethodStartTimeUtcMetricNameString) { inProcMainStartDate = DateTime.FromBinary((long)value); - metrics[Constants.ProcessTimeToMainMetricName] = + metrics[Constants.ProcessTimeToMainMetricNameString] = (inProcMainStartDate.Value - dataPoint.Start).TotalMilliseconds; EnsureMainDuration(metrics, inProcMainStartDate, inProcMainEndDate); EnsureStartupHookOverhead(dataPoint, metrics, inProcStartDate, inProcMainStartDate, @@ -865,10 +871,10 @@ mainEndDate is not null && continue; } - if (name == Constants.MainMethodEndTimeUtcMetricName) + if (name == Constants.MainMethodEndTimeUtcMetricNameString) { inProcMainEndDate = DateTime.FromBinary((long)value); - metrics[Constants.ProcessTimeToMainEndMetricName] = + metrics[Constants.ProcessTimeToMainEndMetricNameString] = (dataPoint.End - inProcMainEndDate.Value).TotalMilliseconds; EnsureMainDuration(metrics, inProcMainStartDate, inProcMainEndDate); EnsureStartupHookOverhead(dataPoint, metrics, inProcStartDate, inProcMainStartDate, @@ -876,21 +882,21 @@ mainEndDate is not null && continue; } - if (name == Constants.ProcessEndTimeUtcMetricName) + if (name == Constants.ProcessEndTimeUtcMetricNameString) { inProcEndDate = DateTime.FromBinary((long)value); - metrics[Constants.ProcessTimeToEndMetricName] = + metrics[Constants.ProcessTimeToEndMetricNameString] = (dataPoint.End - inProcEndDate.Value).TotalMilliseconds; EnsureStartupHookOverhead(dataPoint, metrics, inProcStartDate, inProcMainStartDate, inProcMainEndDate, inProcEndDate); continue; } - if (type == "counter") + if (type == BinaryFileStorage.MetricType.Counter) { metrics[name] = value; } - else if (type is "gauge" or "timer") + else if (type is BinaryFileStorage.MetricType.Gauge or BinaryFileStorage.MetricType.Timer) { ref var oldValue = ref CollectionsMarshal.GetValueRefOrAddDefault(metrics, name, out _); oldValue += value; @@ -899,17 +905,17 @@ mainEndDate is not null && ref CollectionsMarshal.GetValueRefOrAddDefault(metricsCount, name, out _); count++; } - else if (type == "increment") + else if (type == BinaryFileStorage.MetricType.Increment) { ref var oldValue = ref CollectionsMarshal.GetValueRefOrAddDefault(metrics, name, out _); oldValue += value; } } } - } - catch - { - // Error reading metric item, we just skip that item + catch + { + // Error reading metric item, we just skip that item + } } } diff --git a/src/TimeItSharp.StartupHook/Constants.cs b/src/TimeItSharp.StartupHook/Constants.cs index 90df1a5..28b0718 100644 --- a/src/TimeItSharp.StartupHook/Constants.cs +++ b/src/TimeItSharp.StartupHook/Constants.cs @@ -5,15 +5,28 @@ internal static class Constants public const string StartupHookEnvironmentVariable = "DOTNET_STARTUP_HOOKS"; public const string TimeItMetricsTemporalPathEnvironmentVariable = "TIMEIT_METRICS_TEMPORAL_PATH"; public const string TimeItMetricsProcessName = "TIMEIT_METRICS_PROCESS_NAME"; - public const string ProcessTimeToStartMetricName = "process.time_to_start_ms"; - public const string ProcessTimeToMainMetricName = "process.time_to_main_ms"; - public const string ProcessTimeToEndMetricName = "process.time_to_end_ms"; - public const string ProcessTimeToMainEndMetricName = "process.time_to_end_main_ms"; - public const string ProcessInternalDurationMetricName = "process.internal_duration_ms"; - public const string ProcessCorrectedDurationMetricName = "process.corrected_duration_ms"; - public const string ProcessStartupHookOverheadMetricName = "process.startuphook_overhead_ms"; - public const string ProcessStartTimeUtcMetricName = "process.start"; - public const string MainMethodStartTimeUtcMetricName = "main.start"; - public const string MainMethodEndTimeUtcMetricName = "main.end"; - public const string ProcessEndTimeUtcMetricName = "process.end"; + + public const string ProcessTimeToStartMetricNameString = "process.time_to_start_ms"; + public const string ProcessTimeToMainMetricNameString = "process.time_to_main_ms"; + public const string ProcessTimeToEndMetricNameString = "process.time_to_end_ms"; + public const string ProcessTimeToMainEndMetricNameString = "process.time_to_end_main_ms"; + public const string ProcessInternalDurationMetricNameString = "process.internal_duration_ms"; + public const string ProcessCorrectedDurationMetricNameString = "process.corrected_duration_ms"; + public const string ProcessStartupHookOverheadMetricNameString = "process.startuphook_overhead_ms"; + public const string ProcessStartTimeUtcMetricNameString = "process.start"; + public const string MainMethodStartTimeUtcMetricNameString = "main.start"; + public const string MainMethodEndTimeUtcMetricNameString = "main.end"; + public const string ProcessEndTimeUtcMetricNameString = "process.end"; + + public static ReadOnlySpan ProcessTimeToStartMetricName => "process.time_to_start_ms"u8; + public static ReadOnlySpan ProcessTimeToMainMetricName => "process.time_to_main_ms"u8; + public static ReadOnlySpan ProcessTimeToEndMetricName => "process.time_to_end_ms"u8; + public static ReadOnlySpan ProcessTimeToMainEndMetricName => "process.time_to_end_main_ms"u8; + public static ReadOnlySpan ProcessInternalDurationMetricName => "process.internal_duration_ms"u8; + public static ReadOnlySpan ProcessCorrectedDurationMetricName => "process.corrected_duration_ms"u8; + public static ReadOnlySpan ProcessStartupHookOverheadMetricName => "process.startuphook_overhead_ms"u8; + public static ReadOnlySpan ProcessStartTimeUtcMetricName => "process.start"u8; + public static ReadOnlySpan MainMethodStartTimeUtcMetricName => "main.start"u8; + public static ReadOnlySpan MainMethodEndTimeUtcMetricName => "main.end"u8; + public static ReadOnlySpan ProcessEndTimeUtcMetricName => "process.end"u8; } diff --git a/src/TimeItSharp.StartupHook/RuntimeMetrics/BinaryFileStorage.cs b/src/TimeItSharp.StartupHook/RuntimeMetrics/BinaryFileStorage.cs new file mode 100644 index 0000000..970577a --- /dev/null +++ b/src/TimeItSharp.StartupHook/RuntimeMetrics/BinaryFileStorage.cs @@ -0,0 +1,61 @@ +using System.Runtime.CompilerServices; + +namespace TimeItSharp.RuntimeMetrics; + +internal sealed class BinaryFileStorage(string filePath) +{ + private readonly BinaryWriter _binaryWriter = new(new FileStream(filePath, FileMode.Append)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Counter(ReadOnlySpan name, double value) + { + WritePayload(MetricType.Counter, name, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Gauge(ReadOnlySpan name, double value) + { + WritePayload(MetricType.Gauge, name, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Increment(ReadOnlySpan name, double value) + { + WritePayload(MetricType.Increment, name, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Timer(ReadOnlySpan name, double value) + { + WritePayload(MetricType.Timer, name, value); + } + + public void Dispose() + { + lock (_binaryWriter) + { + _binaryWriter.Dispose(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WritePayload(MetricType type, ReadOnlySpan name, double value) + { + lock (_binaryWriter) + { + _binaryWriter.Write((int)7248); // Magic number for identification + _binaryWriter.Write((byte)type); // Metric type + _binaryWriter.Write(name.Length); // Length of the name + _binaryWriter.Write(name); // Name of the metric + _binaryWriter.Write(value); // Value of the metric + } + } + + internal enum MetricType : byte + { + Counter, + Gauge, + Increment, + Timer + } +} \ No newline at end of file diff --git a/src/TimeItSharp.StartupHook/RuntimeMetrics/FileStorage.cs b/src/TimeItSharp.StartupHook/RuntimeMetrics/FileStorage.cs deleted file mode 100644 index 4e9703e..0000000 --- a/src/TimeItSharp.StartupHook/RuntimeMetrics/FileStorage.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System.Globalization; -using System.Runtime.CompilerServices; - -namespace TimeItSharp.RuntimeMetrics; - -internal sealed class FileStorage -{ - private readonly StreamWriter _streamWriter; - - public FileStorage(string filePath) - { - _streamWriter = new StreamWriter(filePath, true); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Counter(string statName, double value) - { - WritePayload("counter", statName, value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Gauge(string statName, double value) - { - WritePayload("gauge", statName, value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Increment(string statName, int value = 1) - { - WritePayload("increment", statName, value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Timer(string statName, double value) - { - WritePayload("timer", statName, value); - } - - public void Dispose() - { - lock (_streamWriter) - { - _streamWriter.Dispose(); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WritePayload(string type, string name, double value) - { - lock (_streamWriter) - { - _streamWriter.Write("{ \"type\": \""); - _streamWriter.Write(type); - _streamWriter.Write("\", "); - - _streamWriter.Write("\"name\": "); - if (name is null) - { - _streamWriter.Write("null, "); - } - else - { - _streamWriter.Write("\""); - _streamWriter.Write(name); - _streamWriter.Write("\", "); - } - - _streamWriter.Write("\"value\": "); - _streamWriter.Write(value.ToString(CultureInfo.InvariantCulture)); - _streamWriter.WriteLine(" }"); - } - } -} diff --git a/src/TimeItSharp.StartupHook/RuntimeMetrics/MetricsName.cs b/src/TimeItSharp.StartupHook/RuntimeMetrics/MetricsName.cs index 2c3ce20..dffbe83 100644 --- a/src/TimeItSharp.StartupHook/RuntimeMetrics/MetricsName.cs +++ b/src/TimeItSharp.StartupHook/RuntimeMetrics/MetricsName.cs @@ -4,43 +4,43 @@ internal static class MetricsNames { - public const string ExceptionsCount = "runtime.dotnet.exceptions.count"; + public static ReadOnlySpan ExceptionsCount => "runtime.dotnet.exceptions.count"u8; - public const string Gen0CollectionsCount = "runtime.dotnet.gc.count.gen0"; - public const string Gen1CollectionsCount = "runtime.dotnet.gc.count.gen1"; - public const string Gen2CollectionsCount = "runtime.dotnet.gc.count.gen2"; - public const string Gen2CompactingCollectionsCount = "runtime.dotnet.gc.count.compacting_gen2"; + public static ReadOnlySpan Gen0CollectionsCount => "runtime.dotnet.gc.count.gen0"u8; + public static ReadOnlySpan Gen1CollectionsCount => "runtime.dotnet.gc.count.gen1"u8; + public static ReadOnlySpan Gen2CollectionsCount => "runtime.dotnet.gc.count.gen2"u8; + public static ReadOnlySpan Gen2CompactingCollectionsCount => "runtime.dotnet.gc.count.compacting_gen2"u8; - public const string GcPauseTime = "runtime.dotnet.gc.pause_time"; - public const string GcMemoryLoad = "runtime.dotnet.gc.memory_load"; + public static ReadOnlySpan GcPauseTime => "runtime.dotnet.gc.pause_time"u8; + public static ReadOnlySpan GcMemoryLoad => "runtime.dotnet.gc.memory_load"u8; - public const string Gen0HeapSize = "runtime.dotnet.gc.size.gen0"; - public const string Gen1HeapSize = "runtime.dotnet.gc.size.gen1"; - public const string Gen2HeapSize = "runtime.dotnet.gc.size.gen2"; - public const string LohSize = "runtime.dotnet.gc.size.loh"; + public static ReadOnlySpan Gen0HeapSize => "runtime.dotnet.gc.size.gen0"u8; + public static ReadOnlySpan Gen1HeapSize => "runtime.dotnet.gc.size.gen1"u8; + public static ReadOnlySpan Gen2HeapSize => "runtime.dotnet.gc.size.gen2"u8; + public static ReadOnlySpan LohSize => "runtime.dotnet.gc.size.loh"u8; - public const string ContentionTime = "runtime.dotnet.threads.contention_time"; - public const string ContentionCount = "runtime.dotnet.threads.contention_count"; + public static ReadOnlySpan ContentionTime => "runtime.dotnet.threads.contention_time"u8; + public static ReadOnlySpan ContentionCount => "runtime.dotnet.threads.contention_count"u8; - public const string ProcessorTime = "runtime.process.processor_time"; - public const string PrivateBytes = "runtime.process.private_bytes"; + public static ReadOnlySpan ProcessorTime => "runtime.process.processor_time"u8; + public static ReadOnlySpan PrivateBytes => "runtime.process.private_bytes"u8; - public const string ThreadPoolWorkersCount = "runtime.dotnet.threads.workers_count"; + public static ReadOnlySpan ThreadPoolWorkersCount => "runtime.dotnet.threads.workers_count"u8; - public const string ThreadsCount = "runtime.dotnet.threads.count"; + public static ReadOnlySpan ThreadsCount => "runtime.dotnet.threads.count"u8; - public const string CommittedMemory = "runtime.dotnet.mem.committed"; + public static ReadOnlySpan CommittedMemory => "runtime.dotnet.mem.committed"u8; - public const string CpuUserTime = "runtime.dotnet.cpu.user"; - public const string CpuSystemTime = "runtime.dotnet.cpu.system"; - public const string CpuPercentage = "runtime.dotnet.cpu.percent"; + public static ReadOnlySpan CpuUserTime => "runtime.dotnet.cpu.user"u8; + public static ReadOnlySpan CpuSystemTime => "runtime.dotnet.cpu.system"u8; + public static ReadOnlySpan CpuPercentage => "runtime.dotnet.cpu.percent"u8; - public const string AspNetCoreCurrentRequests = "runtime.dotnet.aspnetcore.requests.current"; - public const string AspNetCoreFailedRequests = "runtime.dotnet.aspnetcore.requests.failed"; - public const string AspNetCoreTotalRequests = "runtime.dotnet.aspnetcore.requests.total"; - public const string AspNetCoreRequestQueueLength = "runtime.dotnet.aspnetcore.requests.queue_length"; + public static ReadOnlySpan AspNetCoreCurrentRequests => "runtime.dotnet.aspnetcore.requests.current"u8; + public static ReadOnlySpan AspNetCoreFailedRequests => "runtime.dotnet.aspnetcore.requests.failed"u8; + public static ReadOnlySpan AspNetCoreTotalRequests => "runtime.dotnet.aspnetcore.requests.total"u8; + public static ReadOnlySpan AspNetCoreRequestQueueLength => "runtime.dotnet.aspnetcore.requests.queue_length"u8; - public const string AspNetCoreCurrentConnections = "runtime.dotnet.aspnetcore.connections.current"; - public const string AspNetCoreConnectionQueueLength = "runtime.dotnet.aspnetcore.connections.queue_length"; - public const string AspNetCoreTotalConnections = "runtime.dotnet.aspnetcore.connections.total"; + public static ReadOnlySpan AspNetCoreCurrentConnections => "runtime.dotnet.aspnetcore.connections.current"u8; + public static ReadOnlySpan AspNetCoreConnectionQueueLength => "runtime.dotnet.aspnetcore.connections.queue_length"u8; + public static ReadOnlySpan AspNetCoreTotalConnections => "runtime.dotnet.aspnetcore.connections.total"u8; } \ No newline at end of file diff --git a/src/TimeItSharp.StartupHook/RuntimeMetrics/ProcessHelpers.cs b/src/TimeItSharp.StartupHook/RuntimeMetrics/ProcessHelpers.cs index 0fb973f..295e173 100644 --- a/src/TimeItSharp.StartupHook/RuntimeMetrics/ProcessHelpers.cs +++ b/src/TimeItSharp.StartupHook/RuntimeMetrics/ProcessHelpers.cs @@ -9,6 +9,11 @@ internal static class ProcessHelpers { private static readonly Process CurrentProcess = Process.GetCurrentProcess(); + /// + /// Get the process name + /// + public static string ProcessName => CurrentProcess.ProcessName; + /// /// Wrapper around and its property accesses /// diff --git a/src/TimeItSharp.StartupHook/RuntimeMetrics/RuntimeEventListener.cs b/src/TimeItSharp.StartupHook/RuntimeMetrics/RuntimeEventListener.cs index 2fabab5..ef19ac5 100644 --- a/src/TimeItSharp.StartupHook/RuntimeMetrics/RuntimeEventListener.cs +++ b/src/TimeItSharp.StartupHook/RuntimeMetrics/RuntimeEventListener.cs @@ -18,17 +18,20 @@ internal sealed class RuntimeEventListener : EventListener private const int EventContentionStop = 91; private const int EventGcGlobalHeapHistory = 205; - private readonly FileStorage _storage; - private readonly Timing _contentionTime = new(); - private readonly string _delayInSeconds; + private readonly BinaryFileStorage _storage; + private readonly ReadOnlyDictionary _eventCounterIntervalSecDictionary; + private double _contentionTime; private long _contentionCount; private DateTime? _gcStart; - public RuntimeEventListener(FileStorage storage, TimeSpan delay) + public RuntimeEventListener(BinaryFileStorage storage, TimeSpan delay) { _storage = storage; - _delayInSeconds = ((int)delay.TotalSeconds).ToString(); + _eventCounterIntervalSecDictionary = new ReadOnlyDictionary(new Dictionary + { + ["EventCounterIntervalSec"] = ((int)delay.TotalSeconds).ToString() + }); EventSourceCreated += (_, e) => EnableEventSource(e.EventSource); } @@ -36,7 +39,7 @@ public void Refresh() { // Can't use a Timing because Dogstatsd doesn't support local aggregation // It means that the aggregations in the UI would be wrong - _storage.Gauge(MetricsNames.ContentionTime, _contentionTime.Clear()); + _storage.Gauge(MetricsNames.ContentionTime, Interlocked.Exchange(ref _contentionTime, 0)); _storage.Counter(MetricsNames.ContentionCount, Interlocked.Exchange(ref _contentionCount, 0)); _storage.Gauge(MetricsNames.ThreadPoolWorkersCount, ThreadPool.ThreadCount); } @@ -84,7 +87,7 @@ protected override void OnEventWritten(EventWrittenEventArgs eventData) { var durationInNanoseconds = (double)eventData.Payload[2]; - _contentionTime.Time(durationInNanoseconds / 1_000_000); + IncrementTiming(ref _contentionTime, durationInNanoseconds / 1_000_000); Interlocked.Increment(ref _contentionCount); } else if (eventData.EventId == EventGcGlobalHeapHistory) @@ -132,10 +135,7 @@ private void EnableEventSource(EventSource eventSource) } else if (eventSource.Name is AspNetCoreHostingEventSourceName or AspNetCoreKestrelEventSourceName) { - EnableEvents(eventSource, EventLevel.Critical, EventKeywords.All, new Dictionary - { - ["EventCounterIntervalSec"] = _delayInSeconds - }); + EnableEvents(eventSource, EventLevel.Critical, EventKeywords.All, _eventCounterIntervalSecDictionary); } } @@ -167,7 +167,7 @@ private void ExtractCounters(ReadOnlyCollection payload) } } - private static bool TryGetMetricsMapping(string name, out string statName) + private static bool TryGetMetricsMapping(string name, out ReadOnlySpan statName) { switch (name) { @@ -197,4 +197,15 @@ private static bool TryGetMetricsMapping(string name, out string statName) return false; } } + + private static void IncrementTiming(ref double cumulatedTiming, double elapsedMilliseconds) + { + double oldValue; + + do + { + oldValue = cumulatedTiming; + } + while (Math.Abs(Interlocked.CompareExchange(ref cumulatedTiming, oldValue + elapsedMilliseconds, oldValue) - oldValue) > 0.01); + } } \ No newline at end of file diff --git a/src/TimeItSharp.StartupHook/RuntimeMetrics/RuntimeMetricsWriter.cs b/src/TimeItSharp.StartupHook/RuntimeMetrics/RuntimeMetricsWriter.cs index 7431350..ff16ec9 100644 --- a/src/TimeItSharp.StartupHook/RuntimeMetrics/RuntimeMetricsWriter.cs +++ b/src/TimeItSharp.StartupHook/RuntimeMetrics/RuntimeMetricsWriter.cs @@ -7,7 +7,7 @@ namespace TimeItSharp.RuntimeMetrics; internal sealed class RuntimeMetricsWriter : IDisposable { private readonly TimeSpan _delay; - private readonly FileStorage _storage; + private readonly BinaryFileStorage _storage; private readonly Timer _timer; private readonly RuntimeEventListener _listener; private readonly bool _enableProcessMetrics; @@ -17,7 +17,7 @@ internal sealed class RuntimeMetricsWriter : IDisposable private TimeSpan _previousTotalCpu; private int _exceptionCounts; - internal RuntimeMetricsWriter(FileStorage storage, TimeSpan delay) + internal RuntimeMetricsWriter(BinaryFileStorage storage, TimeSpan delay) { _delay = delay; _storage = storage; diff --git a/src/TimeItSharp.StartupHook/RuntimeMetrics/Timing.cs b/src/TimeItSharp.StartupHook/RuntimeMetrics/Timing.cs deleted file mode 100644 index f430f02..0000000 --- a/src/TimeItSharp.StartupHook/RuntimeMetrics/Timing.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace TimeItSharp.RuntimeMetrics; - -// The following code is based on: https://github.com/DataDog/dd-trace-dotnet/blob/master/tracer/src/Datadog.Trace/RuntimeMetrics - -internal sealed class Timing -{ - private double _cumulatedMilliseconds; - - public void Time(double elapsedMilliseconds) - { - double oldValue; - - do - { - oldValue = _cumulatedMilliseconds; - } - while (Math.Abs(Interlocked.CompareExchange(ref _cumulatedMilliseconds, oldValue + elapsedMilliseconds, oldValue) - oldValue) > 0.01); - } - - public double Clear() - { - return Interlocked.Exchange(ref _cumulatedMilliseconds, 0); - } -} \ No newline at end of file diff --git a/src/TimeItSharp.StartupHook/StartupHook.cs b/src/TimeItSharp.StartupHook/StartupHook.cs index dfadf99..12d1aa2 100644 --- a/src/TimeItSharp.StartupHook/StartupHook.cs +++ b/src/TimeItSharp.StartupHook/StartupHook.cs @@ -1,30 +1,29 @@ -using System.Diagnostics; using TimeItSharp; using TimeItSharp.RuntimeMetrics; public sealed class StartupHook { private static RuntimeMetricsWriter? _metricsWriter; - private static FileStorage? _fileStatsd; + private static BinaryFileStorage? _fileStatsd; public static void Initialize() { var startDate = Clock.UtcNow; var metricsPath = Environment.GetEnvironmentVariable(Constants.TimeItMetricsTemporalPathEnvironmentVariable); - if (string.IsNullOrEmpty(metricsPath)) + if (metricsPath == null || metricsPath.Length == 0) { return; } bool enableMetrics; var processName = Environment.GetEnvironmentVariable(Constants.TimeItMetricsProcessName); - if (string.IsNullOrEmpty(processName)) + if (processName == null || processName.Length == 0) { enableMetrics = true; } else { - var currentProcessName = Process.GetCurrentProcess().ProcessName; + var currentProcessName = ProcessHelpers.ProcessName; if (processName.IndexOf(';') == -1) { enableMetrics = string.Equals(currentProcessName, processName, StringComparison.OrdinalIgnoreCase); @@ -38,7 +37,7 @@ public static void Initialize() if (enableMetrics) { - _fileStatsd = new FileStorage(metricsPath); + _fileStatsd = new BinaryFileStorage(metricsPath); _fileStatsd.Gauge(Constants.ProcessStartTimeUtcMetricName, startDate.ToBinary()); _metricsWriter = new RuntimeMetricsWriter(_fileStatsd, TimeSpan.FromMilliseconds(100)); _metricsWriter.PushEvents();