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

String format like MEL #112

Open
sommmen opened this issue Sep 23, 2021 · 1 comment
Open

String format like MEL #112

sommmen opened this issue Sep 23, 2021 · 1 comment

Comments

@sommmen
Copy link

sommmen commented Sep 23, 2021

Hiya,

Consider how logging works in MEL / Serilog:

Logger.LogError(ex, "Could not connect to db for domain {DomainName}, skipping synchronization for this domain!", domain.Name);

This doesn't work in Hangfire.Console with PerformContext because:

public static void WriteLine(this PerformContext context, string format, params object[] args) => ConsoleExtensions.WriteLine(context, string.Format(format, args));

string.format does not accept named substitution. I've just moved away from MEL to Hangfire.Console and now all the logging is broken. {Name} does no longer work, causing errors. {0} does work of course.

I'd think a nice feature would be to reuse the logic from MEL for logging to the hangfire.Console.
This would allow me to more easily write the same line to both hangfire.console and any log consumers.

@sommmen
Copy link
Author

sommmen commented Dec 16, 2021

I mitigated this by using a proxy:

/// <summary>
    /// A logger implementation that logs to both hangfire.console and to a proxied ILogger.
    /// This allows for abstracting the hangfire.console away to MEL.
    /// </summary>
    public class HangfireLogger : IHangfireLogger
    {
        private readonly PerformContext _performContext;
        private readonly ILogger _loggerImplementation;

        /// <summary>
        /// Lock for performContext access. I'm not sure about thread safety so i'm locking to make sure we don't run into issues.
        /// </summary>
        private readonly object _logSyncLock = new();

        public HangfireLogger(PerformContext performContext, ILogger loggerImplementation)
        {
            _performContext = performContext;
            _loggerImplementation = loggerImplementation;
        }

        #region ILogger impl

        public IDisposable BeginScope<TState>(TState state)
        {
            return _loggerImplementation.BeginScope(state);
        }

        public bool IsEnabled(LogLevel logLevel)
        {
            return _loggerImplementation.IsEnabled(logLevel);
        }

        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
        {
            _loggerImplementation.Log(logLevel, eventId, state, exception, formatter);

            if (!IsEnabled(logLevel))
                return;

            var color = GetColor(logLevel);

            lock (_logSyncLock)
            {
                if (color != null)
                    _performContext.SetTextColor(color);
                var formattedString = formatter(state, exception); // TODO BUG apperently this does not destructure objects for some reason? for hangfire.console.
                _performContext.WriteLine(formattedString);
                if (exception != null)
                    _performContext.WriteLine(exception.ToString());
                _performContext.ResetTextColor();
            }
        }

        private static ConsoleTextColor GetColor(LogLevel logLevel) =>
            logLevel switch
            {
                LogLevel.None => null,
                LogLevel.Trace => null,
                LogLevel.Debug => null,
                LogLevel.Information => null,
                LogLevel.Warning => ConsoleTextColor.Yellow,
                LogLevel.Error => ConsoleTextColor.Red,
                LogLevel.Critical => ConsoleTextColor.DarkRed,
                _ => null
            };

        #endregion

        #region Progress bar

        /// <summary>Adds an updateable progress bar to console.</summary>
        /// <param name="value">Initial value</param>
        /// <param name="color">Progress bar color</param>
        public IProgressBar WriteProgressBar(
            int value = 0,
            ConsoleTextColor color = null)
        {
            lock (_logSyncLock)
            {
                return _performContext.WriteProgressBar(value, color);
            }
        }

        /// <summary>Adds an updateable named progress bar to console.</summary>
        /// <param name="name">Name</param>
        /// <param name="value">Initial value</param>
        /// <param name="color">Progress bar color</param>
        public IProgressBar WriteProgressBar(
            string name,
            double value = 0.0,
            ConsoleTextColor color = null)
        {
            lock (_logSyncLock)
            {
                return _performContext.WriteProgressBar(name, value, color);
            }
        }

        #endregion

    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant