< Summary

Class:Azure.Core.TestFramework.TestLogger
Assembly:Azure.Core.TestFramework
File(s):C:\Git\azure-sdk-for-net\sdk\core\Azure.Core.TestFramework\src\TestLogger.cs
Covered lines:0
Uncovered lines:42
Coverable lines:42
Total lines:135
Line coverage:0% (0 of 42)
Covered branches:0
Total branches:24
Branch coverage:0% (0 of 24)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
get_Listener()-0%100%
.ctor()-0%100%
LogEvent(...)-0%0%
GetPayload(...)-0%0%
Dispose()-0%0%

File(s)

C:\Git\azure-sdk-for-net\sdk\core\Azure.Core.TestFramework\src\TestLogger.cs

#LineLine coverage
 1// Copyright (c) Microsoft Corporation. All rights reserved.
 2// Licensed under the MIT License.
 3
 4using System;
 5using System.Collections.Generic;
 6using System.Diagnostics;
 7using System.Diagnostics.Tracing;
 8using System.Linq;
 9using System.Text;
 10using Azure.Core.Diagnostics;
 11using NUnit.Framework;
 12using NUnit.Framework.Interfaces;
 13
 14namespace Azure.Core.TestFramework
 15{
 16    /// <summary>
 17    /// The TestLogger listens for the AzureSDK logging event source and traces
 18    /// the output so it's easy to view the logs when testing.
 19    ///
 20    /// We create an instance in SearchTestBase when we want to log test
 21    /// output.
 22    /// </summary>
 23    internal class TestLogger : IDisposable
 24    {
 25        /// <summary>
 26        /// EventSource listener for AzureSDK events.
 27        /// </summary>
 028        private AzureEventSourceListener Listener { get; }
 29
 30        /// <summary>
 31        /// Start collecting AzureSDK events to log.
 32        /// </summary>
 033        public TestLogger()
 34        {
 035            Listener = new AzureEventSourceListener(
 036                (e, _) => LogEvent(e),
 037                EventLevel.Verbose);
 038        }
 39
 40        /// <summary>
 41        /// Trace any SDK events.
 42        /// </summary>
 43        /// <param name="args">Event arguments.</param>
 44        public void LogEvent(EventWrittenEventArgs args)
 45        {
 046            var category = args.EventName;
 047            IDictionary<string, string> payload = GetPayload(args);
 48
 49            // If there's a request ID, use it after the category
 050            var message = new StringBuilder();
 051            if (payload.TryGetValue("requestId", out var requestId))
 52            {
 053                payload.Remove("requestId");
 054                message.Append(requestId);
 55            }
 056            message.AppendLine();
 57
 58            // Add the rest of the payload
 059            foreach (KeyValuePair<string, string> arg in payload)
 60            {
 061                message.AppendFormat("  {0}: ", arg.Key);
 62
 63                // Don't indent the content's lines
 064                if (arg.Key == "content" && arg.Value.Length > 0)
 65                {
 066                    message.AppendLine("\n" + arg.Value);
 067                    continue;
 68                }
 69
 070                var lines = arg.Value.Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
 071                if (lines.Length == 1)
 72                {
 73                    // If there's only one line, write it after the name
 074                    message.AppendLine(lines[0]);
 75                }
 76                else
 77                {
 78                    // Otherwise add a newline and indent each nested line
 079                    message.AppendLine();
 080                    foreach (var line in lines.Select(l => $"    {l}"))
 81                    {
 082                        message.AppendLine(line);
 83                    }
 84                }
 85            }
 86
 87            // Dump the message and category
 088            Trace.WriteLine(message, category);
 89
 90            // Add a line to separate requests/responses within a single test
 091            message.AppendLine();
 92            // Output the request/response
 093            TestContext.Out.WriteLine(message.ToString());
 094        }
 95
 96        /// <summary>
 97        /// Convert the event arguments into a dictionary of strings.
 98        /// </summary>
 99        /// <param name="args">The event arguments.</param>
 100        /// <returns>A dictionary of strings.</returns>
 101        private static IDictionary<string, string> GetPayload(EventWrittenEventArgs args)
 102        {
 0103            var payload = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
 0104            for (var i = 0; i < args.Payload.Count; i++)
 105            {
 0106                var name = args.PayloadNames[i];
 0107                var value = "";
 0108                switch (args.Payload[i])
 109                {
 110                    case null:
 111                        break;
 112                    case string s:
 0113                        value = s;
 0114                        break;
 115                    case byte[] content:
 0116                        value = Encoding.UTF8.GetString(content);
 117                        // Control characters mess up copy/pasting so we'll
 118                        // swap them with the SUB character
 0119                        value = new string(value.Select(ch => !char.IsControl(ch) ? ch : '�').ToArray());
 0120                        break;
 121                    default:
 0122                        value = args.Payload[i].ToString();
 123                        break;
 124                }
 0125                payload.Add(name, value);
 126            }
 0127            return payload;
 128        }
 129
 130        /// <summary>
 131        /// Cleans up the <see cref="AzureEventSourceListener"/> instance.
 132        /// </summary>
 0133        public void Dispose() => Listener?.Dispose();
 134    }
 135}