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

Allow overriding the Part B "name" field value in GenevaLogExporter #1367

Merged
merged 6 commits into from
Sep 22, 2023
Merged
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
3 changes: 3 additions & 0 deletions src/OpenTelemetry.Exporter.Geneva/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

* Allow overriding the Part B "name" field value in GenevaLogExporter.
([#1367](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/1367))

## 1.6.0

Released 2023-Sep-09
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,12 +259,9 @@ internal int SerializeLogRecord(LogRecord logRecord)
cursor = MessagePackSerializer.SerializeUInt8(buffer, cursor, GetSeverityNumber(logLevel));
cntFields += 1;

cursor = MessagePackSerializer.SerializeAsciiString(buffer, cursor, "name");
cursor = MessagePackSerializer.SerializeUnicodeString(buffer, cursor, categoryName);
cntFields += 1;

bool hasEnvProperties = false;
bool bodyPopulated = false;
bool namePopulated = false;
for (int i = 0; i < listKvp?.Count; i++)
{
var entry = listKvp[i];
Expand All @@ -285,6 +282,17 @@ internal int SerializeLogRecord(LogRecord logRecord)
if (entry.Value != null)
{
// null is not supported.
if (string.Equals(entry.Key, "name", StringComparison.Ordinal))
{
if (!(entry.Value is string))
{
// name must be string according to Part B in Common Schema. Skip serializing this field otherwise
continue;
}

namePopulated = true;
}

cursor = MessagePackSerializer.SerializeUnicodeString(buffer, cursor, entry.Key);
cursor = MessagePackSerializer.Serialize(buffer, cursor, entry.Value);
cntFields += 1;
Expand All @@ -297,6 +305,13 @@ internal int SerializeLogRecord(LogRecord logRecord)
}
}

if (!namePopulated)
{
cursor = MessagePackSerializer.SerializeAsciiString(buffer, cursor, "name");
cursor = MessagePackSerializer.SerializeUnicodeString(buffer, cursor, categoryName);
cntFields += 1;
}

if (!bodyPopulated && logRecord.FormattedMessage != null)
{
cursor = MessagePackSerializer.SerializeAsciiString(buffer, cursor, "body");
Expand Down
23 changes: 20 additions & 3 deletions src/OpenTelemetry.Exporter.Geneva/TLDExporter/TldLogExporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,6 @@ internal void SerializeLogRecord(LogRecord logRecord)

eb.AddCountedString("severityText", logLevels[(int)logLevel]);
eb.AddUInt8("severityNumber", GetSeverityNumber(logLevel));
eb.AddCountedAnsiString("name", categoryName, Encoding.UTF8);

var eventId = logRecord.EventId;
if (eventId != default)
Expand All @@ -312,6 +311,7 @@ internal void SerializeLogRecord(LogRecord logRecord)

byte hasEnvProperties = 0;
bool bodyPopulated = false;
bool namePopulated = false;

byte partCFieldsCountFromState = 0;
var kvpArrayForPartCFields = partCFields.Value;
Expand Down Expand Up @@ -342,8 +342,20 @@ internal void SerializeLogRecord(LogRecord logRecord)
if (entry.Value != null)
{
// null is not supported.
kvpArrayForPartCFields[partCFieldsCountFromState] = new(entry.Key, entry.Value);
partCFieldsCountFromState++;
if (string.Equals(entry.Key, "name", StringComparison.Ordinal))
{
if (entry.Value is string nameValue)
{
// name must be string according to Part B in Common Schema. Skip serializing this field otherwise
eb.AddCountedAnsiString("name", nameValue, Encoding.UTF8);
namePopulated = true;
}
}
else
{
kvpArrayForPartCFields[partCFieldsCountFromState] = new(entry.Key, entry.Value);
partCFieldsCountFromState++;
}
}
}
else
Expand All @@ -366,6 +378,11 @@ internal void SerializeLogRecord(LogRecord logRecord)
}
}

if (!namePopulated)
{
eb.AddCountedAnsiString("name", categoryName, Encoding.UTF8);
}

if (!bodyPopulated && logRecord.FormattedMessage != null)
{
eb.AddCountedAnsiString("body", logRecord.FormattedMessage, Encoding.UTF8);
Expand Down
136 changes: 136 additions & 0 deletions test/OpenTelemetry.Exporter.Geneva.Tests/GenevaLogExporterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1132,6 +1132,142 @@ public void SerializationTestForEventName(EventNameExportMode eventNameExportMod
}
}

[Theory]
[InlineData(false, false, "Custom name")]
[InlineData(false, false, "")]
[InlineData(false, false, null)]
[InlineData(false, false, 12345)]
[InlineData(true, false, "Custom name")]
[InlineData(true, true, "Custom name")]
jdom marked this conversation as resolved.
Show resolved Hide resolved
[InlineData(true, true, 12345)]
[InlineData(true, false, 12345)]
public void SerializationTestForPartBName(bool hasCustomFields, bool hasNameInCustomFields, object customNameValue)
{
// ARRANGE
string path = string.Empty;
Socket server = null;
var logRecordList = new List<LogRecord>();
try
{
var exporterOptions = new GenevaExporterOptions();

if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
exporterOptions.ConnectionString = "EtwSession=OpenTelemetry";
}
else
{
path = GenerateTempFilePath();
exporterOptions.ConnectionString = "Endpoint=unix:" + path;
var endpoint = new UnixDomainSocketEndPoint(path);
server = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.IP);
server.Bind(endpoint);
server.Listen(1);
}

if (hasCustomFields)
{
if (hasNameInCustomFields)
{
exporterOptions.CustomFields = new string[] { "name", "Key1" };
}
else
{
exporterOptions.CustomFields = new string[] { "Key1" };
}
}

using var loggerFactory = LoggerFactory.Create(builder => builder
.AddOpenTelemetry(options =>
{
options.AddGenevaLogExporter(options =>
{
options.ConnectionString = exporterOptions.ConnectionString;
options.CustomFields = exporterOptions.CustomFields;
});
options.AddInMemoryExporter(logRecordList);
}));

// Create a test exporter to get MessagePack byte data to validate if the data was serialized correctly.
using var exporter = new MsgPackLogExporter(exporterOptions);

// Emit a LogRecord and grab a copy of the LogRecord from the collection passed to InMemoryExporter
var logger = loggerFactory.CreateLogger<GenevaLogExporterTests>();

// ACT
// This is treated as structured logging as the state can be converted to IReadOnlyList<KeyValuePair<string, object>>

var state = new List<KeyValuePair<string, object>>()
{
new KeyValuePair<string, object>("Key1", "Value1"),
new KeyValuePair<string, object>("Key2", "Value2"),
};

if (customNameValue != null)
{
state.Add(new KeyValuePair<string, object>("name", customNameValue));
}

logger.Log(
LogLevel.Information,
default,
state,
null,
null);

// VALIDATE
Assert.Single(logRecordList);
var m_buffer = typeof(MsgPackLogExporter).GetField("m_buffer", BindingFlags.NonPublic | BindingFlags.Static).GetValue(exporter) as ThreadLocal<byte[]>;
_ = exporter.SerializeLogRecord(logRecordList[0]);
object fluentdData = MessagePack.MessagePackSerializer.Deserialize<object>(m_buffer.Value, MessagePack.Resolvers.ContractlessStandardResolver.Instance);
var signal = (fluentdData as object[])[0] as string;
var TimeStampAndMappings = ((fluentdData as object[])[1] as object[])[0];
var mapping = (TimeStampAndMappings as object[])[1] as Dictionary<object, object>;
var actualNameValue = mapping["name"];

if (!hasCustomFields || hasNameInCustomFields)
{
if (customNameValue is string stringNameValue)
{
Assert.Equal(stringNameValue, actualNameValue);
}
else
{
Assert.Equal(typeof(GenevaLogExporterTests).FullName, actualNameValue);
}
}
else
{
Assert.Equal(typeof(GenevaLogExporterTests).FullName, actualNameValue);
if (customNameValue != null)
{
var envProperties = mapping["env_properties"] as Dictionary<object, object>;
if (customNameValue is int customNameNumber)
{
Assert.Equal(Convert.ToInt32(envProperties["name"]), customNameNumber);
}
else
{
Assert.Equal((string)envProperties["name"], (string)customNameValue);
}
}
}

logRecordList.Clear();
}
finally
{
server?.Dispose();
try
{
File.Delete(path);
}
catch
{
}
}
}

[Fact]
public void SerializationTestForEventId()
{
Expand Down
Loading