File: PackCollectorLogger.cs
Web Access
Project: src\src\nuget-client\src\NuGet.Core\NuGet.Commands\NuGet.Commands.csproj (NuGet.Commands)
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

#nullable disable

using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
using NuGet.Common;
using NuGet.ProjectModel;

namespace NuGet.Commands
{
    public class PackCollectorLogger : LoggerBase
    {
        private readonly ConcurrentQueue<ILogMessage> _errors;
        private ILogger _innerLogger;

        // Project level warnings properties
        public WarningProperties WarningProperties { get; set; }

        // Package specific warning property
        private readonly PackCommand.PackageSpecificWarningProperties _packageSpecificWarningProperties;

        public IEnumerable<ILogMessage> Errors => _errors.ToArray();

        public PackCollectorLogger(ILogger innerLogger, WarningProperties warningProperties)
            : this(innerLogger, warningProperties, null) { }

        public PackCollectorLogger(ILogger innerLogger, WarningProperties warningProperties, PackCommand.PackageSpecificWarningProperties packageSpecificWarningProperties)
        {
            _innerLogger = innerLogger;
            WarningProperties = warningProperties;
            _packageSpecificWarningProperties = packageSpecificWarningProperties;
            _errors = new ConcurrentQueue<ILogMessage>();
        }

        public override void Log(ILogMessage message)
        {
            // check if the message is a warning and it is suppressed
            if (!IsWarningSuppressed(message))
            {
                // if the message is not suppressed then check if it needs to be upgraded to an error
                UpgradeWarningToErrorIfNeeded(message);

                if (CollectMessage(message.Level))
                {
                    _errors.Enqueue(message);
                }

                if (DisplayMessage(message))
                {
                    _innerLogger.Log(message);
                }
            }
        }

        public override Task LogAsync(ILogMessage message)
        {
            // check if the message is a warning and it is suppressed
            if (!IsWarningSuppressed(message))
            {
                // if the message is not suppressed then check if it needs to be upgraded to an error
                UpgradeWarningToErrorIfNeeded(message);

                if (CollectMessage(message.Level))
                {
                    _errors.Enqueue(message);
                }

                if (DisplayMessage(message))
                {
                    return _innerLogger.LogAsync(message);
                }
            }

            return Task.CompletedTask;
        }

        private bool IsWarningSuppressed(ILogMessage message)
        {
            if (message.Level == LogLevel.Warning)
            {
                // If the WarningPropertiesCollection is present then test if the warning is suppressed in
                // project wide no warn
                if (WarningPropertiesCollection.ApplyProjectWideNoWarnProperties(message, warningProperties: WarningProperties))
                {
                    return true;
                }
                else
                {
                    // Use packagereference warning properties only if the project does not suppress the warning
                    // In packagereference warning properties look at only the package specific ones as all properties are per package reference.
                    IPackLogMessage packLogMessage = message as IPackLogMessage;

                    if (packLogMessage != null)
                    {
                        return _packageSpecificWarningProperties?.ApplyNoWarnProperties(packLogMessage) == true;
                    }
                }
            }

            return false;
        }

        /// <summary>
        /// This method upgrades the warning to an error if the project wide warning properties have set the code in WarningsAsErrors or
        /// set TreatWarningsAsErrors to true
        /// </summary>
        /// <param name="message">ILogMessage to be logged as an error or warning.</param>
        /// <returns>bool indicating if the message should be suppressed.</returns>
        private void UpgradeWarningToErrorIfNeeded(ILogMessage message)
        {
            WarningPropertiesCollection.ApplyProjectWideWarningsAsErrorProperties(message, WarningProperties);
        }

        /// <summary>
        /// Decides if the log should be passed to the inner logger.
        /// </summary>
        /// <param name="message">ILogMessage to be logged.</param>
        /// <returns>bool indicating if this message should be logged.</returns>
        private bool DisplayMessage(ILogMessage message)
        {
            return (message.Level >= VerbosityLevel);
        }
    }
}