Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LogRecord pipeline addition #5124

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public CounterConfiguration(CounterFilter filter)

internal record struct ProviderAndCounter(string ProviderName, string CounterName);

internal static class TraceEventExtensions
internal static partial class TraceEventExtensions
{
private static Dictionary<ProviderAndCounter, CounterMetadata> counterMetadataByName = new();
private static Dictionary<int, CounterMetadata> counterMetadataById = new();
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable enable

using System.Threading.Tasks;
using System.Threading;
using System.Collections.Generic;
using System;

namespace Microsoft.Diagnostics.Monitoring.EventPipe
{
internal interface ILogRecordLogger
{
void Log(
in LogRecord log,
ReadOnlySpan<KeyValuePair<string, object?>> attributes,
in LogRecordScopeContainer scopes);

Task PipelineStarted(CancellationToken token);
Task PipelineStopped(CancellationToken token);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
using System.Collections;
using System.Collections.Generic;
using System.Text.Json;
using System.Runtime.InteropServices;
using System.Buffers;

namespace Microsoft.Diagnostics.Monitoring.EventPipe
{
Expand All @@ -13,12 +15,21 @@ public class LogObject : IReadOnlyList<KeyValuePair<string, object>>, IStateWith
public static readonly Func<object, Exception, string> Callback = (state, exception) => ((LogObject)state).ToString();

private readonly string _formattedMessage;
private List<KeyValuePair<string, object>> _items = new();
private KeyValuePair<string, object>[] _items = new KeyValuePair<string, object>[8];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why move from List to array?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would CollectionsMarshal.AsSpan work here?


public LogObject(JsonElement element, string formattedMessage = null)
{
int index = 0;

foreach (JsonProperty item in element.EnumerateObject())
{
if (index >= _items.Length)
{
KeyValuePair<string, object>[] newArray = new KeyValuePair<string, object>[_items.Length * 2];
_items.CopyTo(newArray, 0);
_items = newArray;
}

switch (item.Value.ValueKind)
{
case JsonValueKind.Undefined:
Expand All @@ -28,35 +39,42 @@ public LogObject(JsonElement element, string formattedMessage = null)
case JsonValueKind.Array:
break;
case JsonValueKind.String:
_items.Add(new KeyValuePair<string, object>(item.Name, item.Value.GetString()));
_items[index++] = new KeyValuePair<string, object>(item.Name, item.Value.GetString());
break;
case JsonValueKind.Number:
_items.Add(new KeyValuePair<string, object>(item.Name, item.Value.GetInt32()));
_items[index++] = new KeyValuePair<string, object>(item.Name, item.Value.GetInt32());
break;
case JsonValueKind.False:
case JsonValueKind.True:
_items.Add(new KeyValuePair<string, object>(item.Name, item.Value.GetBoolean()));
_items[index++] = new KeyValuePair<string, object>(item.Name, item.Value.GetBoolean());
break;
case JsonValueKind.Null:
_items.Add(new KeyValuePair<string, object>(item.Name, null));
_items[index++] = new KeyValuePair<string, object>(item.Name, null);
break;
default:
break;
}
}

_formattedMessage = formattedMessage;
Count = index;
}

public KeyValuePair<string, object> this[int index] => _items[index];

public int Count => _items.Count;
public int Count { get; private set; }

public DateTime Timestamp { get; internal set; }

internal ReadOnlySpan<KeyValuePair<string, object>> ToSpan()
=> new(_items, 0, Count);

public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
return _items.GetEnumerator();
for (int i = 0; i < Count; i++)
{
yield return this[i];
}
}

IEnumerator IEnumerable.GetEnumerator()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable enable

using System;
using System.Diagnostics;
using Microsoft.Extensions.Logging;

namespace Microsoft.Diagnostics.Monitoring.EventPipe
{
internal readonly struct LogRecord
{
public LogRecord(
DateTime timestamp,
string categoryName,
LogLevel logLevel,
EventId eventId,
in LogRecordException exception,
string? formattedMessage,
string? messageTemplate,
ActivityTraceId traceId,
ActivitySpanId spanId,
ActivityTraceFlags traceFlags)
{
if (string.IsNullOrEmpty(categoryName))
{
throw new ArgumentNullException(nameof(categoryName));
}

Timestamp = timestamp;
CategoryName = categoryName;
LogLevel = logLevel;
EventId = eventId;
Exception = exception;
FormattedMessage = formattedMessage;
MessageTemplate = messageTemplate;
TraceId = traceId;
SpanId = spanId;
TraceFlags = traceFlags;
}

public readonly DateTime Timestamp;

public readonly string CategoryName;

public readonly LogLevel LogLevel;

public readonly EventId EventId;

public readonly LogRecordException Exception;

public readonly string? FormattedMessage;

public readonly string? MessageTemplate;

public readonly ActivityTraceId TraceId;

public readonly ActivitySpanId SpanId;

public readonly ActivityTraceFlags TraceFlags;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable enable

using System;
using System.Diagnostics;

namespace Microsoft.Diagnostics.Monitoring.EventPipe
{
internal readonly struct LogRecordException : IEquatable<LogRecordException>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a record struct might give you these semantics automatically.

{
public LogRecordException(
string? exceptionType,
string? message,
string? stackTrace)
{
ExceptionType = exceptionType;
Message = message;
StackTrace = stackTrace;
}

public readonly string? ExceptionType;

public readonly string? Message;

public readonly string? StackTrace;

public bool Equals(LogRecordException other)
{
return ExceptionType == other.ExceptionType
&& Message == other.Message
&& StackTrace == other.StackTrace;
}

public override bool Equals(object obj)
=> obj is LogRecordException ex && Equals(ex);

public override int GetHashCode()
{
HashCode hash = default;

hash.Add(ExceptionType);
hash.Add(Message);
hash.Add(StackTrace);

return hash.ToHashCode();
}

public static bool operator ==(LogRecordException left, LogRecordException right)
=> left.Equals(right);

public static bool operator !=(LogRecordException left, LogRecordException right)
=> !left.Equals(right);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable enable

using System;
using System.Collections.Generic;

namespace Microsoft.Diagnostics.Monitoring.EventPipe
{
internal ref struct LogRecordPayload
{
public LogRecord LogRecord;

public ReadOnlySpan<KeyValuePair<string, object?>> Attributes;

public ReadOnlySpan<LogObject> Scopes;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable enable

using System.Collections.Generic;
using System;

namespace Microsoft.Diagnostics.Monitoring.EventPipe
{
internal readonly ref struct LogRecordScopeContainer
{
private readonly ReadOnlySpan<LogObject> _Scopes;

public LogRecordScopeContainer(
ReadOnlySpan<LogObject> scopes)
{
_Scopes = scopes;
}

public void ForEachScope<T>(LogRecordScopeAction<T> callback, ref T state)
{
foreach (LogObject scope in _Scopes)
{
callback(scope.ToSpan(), ref state);
}
}

public delegate void LogRecordScopeAction<T>(
ReadOnlySpan<KeyValuePair<string, object?>> attributes,
ref T state);
}
}
Loading