File: DataCollection\TestPlatformDataCollectionLogger.cs
Web Access
Project: src\src\vstest\src\Microsoft.TestPlatform.Common\Microsoft.TestPlatform.Common.csproj (Microsoft.VisualStudio.TestPlatform.Common)
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Globalization;

using Microsoft.VisualStudio.TestPlatform.Common.DataCollector.Interfaces;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.DataCollection;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;

namespace Microsoft.VisualStudio.TestPlatform.Common.DataCollector;

/// <summary>
/// Class used by data collectors to send messages to the client
/// </summary>
internal class TestPlatformDataCollectionLogger : DataCollectionLogger
{
    /// <summary>
    /// DataCollector's config info.
    /// </summary>
    private readonly DataCollectorConfig _dataCollectorConfig;

    /// <summary>
    /// Message sink.
    /// </summary>
    private readonly IMessageSink _sink;

    /// <summary>
    /// Initializes a new instance of the <see cref="TestPlatformDataCollectionLogger"/> class.
    /// </summary>
    /// <param name="sink">
    /// The underlying raw IMessageSink.  Cannot be null.
    /// </param>
    /// <param name="dataCollectorConfig">
    /// The data Collector Information.
    /// </param>
    internal TestPlatformDataCollectionLogger(IMessageSink sink, DataCollectorConfig dataCollectorConfig)
    {
        _sink = sink ?? throw new ArgumentNullException(nameof(sink));
        _dataCollectorConfig = dataCollectorConfig ?? throw new ArgumentNullException(nameof(dataCollectorConfig));
    }

    /// <inheritdoc/>
    public override void LogError(DataCollectionContext context, string text)
    {
        ValidateArg.NotNull(context, nameof(context));
        ValidateArg.NotNull(text, nameof(text));

        EqtTrace.Error(
            "Data collector '{0}' logged the following error: {1}",
            _dataCollectorConfig.TypeUri,
            text);

        SendTextMessage(context, text, TestMessageLevel.Error);
    }

    /// <inheritdoc/>
    public override void LogError(DataCollectionContext context, string text, Exception exception)
    {
        ValidateArg.NotNull(context, nameof(context));
        ValidateArg.NotNull(text, nameof(text));
        ValidateArg.NotNull(exception, nameof(exception));

        // Make sure the data collection context is not a derived data collection context.  This
        // is done to safeguard from 3rd parties creating their own data collection contexts.
        if (context.GetType() != typeof(DataCollectionContext))
        {
            throw new InvalidOperationException(Resources.Resources.WrongDataCollectionContextType);
        }

        EqtTrace.Error(
            "Data collector '{0}' logged the following error:" + Environment.NewLine +
            "Description:            {1}" + Environment.NewLine +
            "Exception type:         {2}" + Environment.NewLine + "Exception message:      {3}"
            + Environment.NewLine + "Exception stack trace:  {4}",
            _dataCollectorConfig.TypeUri,
            text,
            exception.GetType(),
            exception.Message,
            exception.StackTrace);

        // Currently there is one type of DataCollectionMessage sent across client for all message kind.
        // If required new type can be created for different message type.
        var message = string.Format(
            CultureInfo.CurrentCulture,
            Resources.Resources.ReportDataCollectorException,
            exception.GetType(),
            exception.Message,
            text);
        SendTextMessage(context, message, TestMessageLevel.Error);
    }

    /// <inheritdoc/>
    public override void LogWarning(DataCollectionContext context, string text)
    {
        ValidateArg.NotNull(context, nameof(context));
        ValidateArg.NotNull(text, nameof(text));

        EqtTrace.Warning(
            "Data collector '{0}' logged the following warning: {1}",
            _dataCollectorConfig.TypeUri,
            text);

        SendTextMessage(context, text, TestMessageLevel.Warning);
    }

    /// <summary>
    /// Sends text to message sink.
    /// </summary>
    /// <param name="context">
    /// The context.
    /// </param>
    /// <param name="text">
    /// The text.
    /// </param>
    /// <param name="level">
    /// The level.
    /// </param>
    /// <exception cref="InvalidOperationException">Throws InvalidOperationException.
    /// </exception>
    private void SendTextMessage(DataCollectionContext context, string text, TestMessageLevel level)
    {
        ValidateArg.NotNull(context, nameof(context));
        ValidateArg.NotNull(text, nameof(text));
        TPDebug.Assert(
            level is >= TestMessageLevel.Informational and <= TestMessageLevel.Error,
            "Invalid level: " + level);

        // Make sure the data collection context is not a derived data collection context.  This
        // is done to safeguard from 3rd parties creating their own data collection contexts.
        if (context.GetType() != typeof(DataCollectionContext))
        {
            throw new InvalidOperationException(Resources.Resources.WrongDataCollectionContextType);
        }

        var args = new DataCollectionMessageEventArgs(level, text);
        args.Uri = _dataCollectorConfig.TypeUri;
        args.FriendlyName = _dataCollectorConfig.FriendlyName;
        if (context.HasTestCase)
        {
            args.TestCaseId = context.TestExecId.Id;
        }

        _sink.SendMessage(args);
    }
}