diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..5c19a07622 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "cSpell.words": [ + "fluentdData" + ] +} diff --git a/test/OpenTelemetry.Exporter.Geneva.Tests/LogSerializationTests.cs b/test/OpenTelemetry.Exporter.Geneva.Tests/LogSerializationTests.cs new file mode 100644 index 0000000000..69cbf74d4a --- /dev/null +++ b/test/OpenTelemetry.Exporter.Geneva.Tests/LogSerializationTests.cs @@ -0,0 +1,149 @@ +// +// Copyright The OpenTelemetry Authors +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Sockets; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Threading; +using Microsoft.Extensions.Logging; +using OpenTelemetry.Logs; +using Xunit; +using Xunit.Abstractions; + +namespace OpenTelemetry.Exporter.Geneva.Tests; + +public class LogSerializationTests +{ + /* + Run from the current directory: + dotnet test -f net6.0 --filter FullyQualifiedName~LogSerializationTests -l "console;verbosity=detailed" + */ + private readonly ITestOutputHelper output; + + public LogSerializationTests(ITestOutputHelper output) + { + this.output = output; + } + + [Fact] + public void SerializationTestForException() + { + var exceptionMessage = "Exception Message"; + var exportedFields = GetExportedFieldsAfterLogging(logger => logger.Log( + logLevel: LogLevel.Information, + eventId: default, + state: null, + exception: new Exception(exceptionMessage), + formatter: null)); + + PrintFields(this.output, exportedFields); + var actualExceptionMessage = exportedFields["env_ex_msg"]; + Assert.Equal(exceptionMessage, actualExceptionMessage); + } + + private static void PrintFields(ITestOutputHelper output, Dictionary fields) + { + foreach (var field in fields) + { + output.WriteLine($"{field.Key}:{field.Value}"); + } + } + + private static Dictionary GetExportedFieldsAfterLogging(Action doLog) + { + Socket server = null; + string path = string.Empty; + try + { + var logRecordList = new List(); + using var loggerFactory = LoggerFactory.Create(builder => builder + .AddOpenTelemetry(options => + { + options.AddInMemoryExporter(logRecordList); + }) + .AddFilter(typeof(GenevaLogExporterTests).FullName, LogLevel.Trace)); // Enable all LogLevels + + var logger = loggerFactory.CreateLogger(); + doLog(logger); + + Assert.Single(logRecordList); + 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); + } + + using var exporter = new MsgPackLogExporter(exporterOptions); + var m_buffer = typeof(MsgPackLogExporter).GetField("m_buffer", BindingFlags.NonPublic | BindingFlags.Static).GetValue(exporter) as ThreadLocal; + _ = exporter.SerializeLogRecord(logRecordList[0]); + object fluentdData = MessagePack.MessagePackSerializer.Deserialize(m_buffer.Value, MessagePack.Resolvers.ContractlessStandardResolver.Instance); + + return GetFields(fluentdData); + } + finally + { + server?.Dispose(); + try + { + File.Delete(path); + } + catch + { + } + } + } + + private static string GenerateTempFilePath() + { + while (true) + { + string path = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + if (!File.Exists(path)) + { + return path; + } + } + } + + private static Dictionary GetFields(object fluentdData) + { + /* Fluentd Forward Mode: + [ + "Log", + [ + [ , { "env_ver": "4.0", ... } ] + ], + { "TimeFormat": "DateTime" } + ] + */ + + var TimeStampAndMappings = ((fluentdData as object[])[1] as object[])[0]; + var mapping = (TimeStampAndMappings as object[])[1] as Dictionary; + return mapping; + } +}