首页 > 解决方案 > 如何将用户信息添加到 nlog 错误日志中

问题描述

我最近开始使用 nLog 在我的应用程序上记录错误。但是,我在这里看看是否有任何方法可以将有关当前在应用程序上登录的用户的一些信息添加到日志布局中,如果用户当前在异常发生时登录到应用程序上。

目前我有以下布局:

<target xsi:type="File" name="errorlogs" fileName="./logs/error-logs/${shortdate}.log"
        layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" />

但是,如果在应用程序上有登录用户时发生异常,我希望添加当前登录用户的电子邮件。

我的应用程序在 ASP.Net Core 3.1 上运行。

如何将其添加到布局中。

谢谢

标签: c#exceptionnlogasp.net-core-3.1error-logging

解决方案


也许不是 100% 适合您的问题,因为您的问题依赖于用户登录到您的应用程序,并且您的应用程序在他们的用户上下文下运行,但以下内容在“正常”应用程序编程中对我有用......

创建一个新的公共静态类。我称我为“日志记录”。如果您的程序集还没有它,请添加对 NLog 的引用。

输出格式是免费提供的 Microsoft CMTrace 实用程序使用的格式。可在此处获取:CMTrace 下载链接

添加以下内容:

    public static class Logging
{
    #region Fields

    private static bool _IsSetup = false;
    private static Logger _Log = LogManager.GetCurrentClassLogger();

    #endregion

    #region Private Methods

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static string GetCurrentMethod()
    {
        StackTrace st = new StackTrace();
        int FrameNumber = 1;
        StackFrame sf = st.GetFrame(FrameNumber);  // Get the previous stack frame
        string MethodName = sf.GetMethod().Name;
        string ClassName = sf.GetMethod().ReflectedType.FullName;

        while (MethodName == "Log" || MethodName.StartsWith("Write")) // If it's the "Log" or "Write" method calling this, get the method before that one.
        {
            FrameNumber++;

            if (FrameNumber < 6)
            {
                try
                {
                    MethodName = st.GetFrame(FrameNumber).GetMethod().Name;
                    ClassName = st.GetFrame(FrameNumber).GetMethod().ReflectedType.FullName;
                }
                catch
                {
                }
            }
            else // Prevent an infinite loop
            {
                MethodName = "Unknown Method";
                ClassName = "Unknown Class";
            }
        }
        return ClassName + "." + MethodName;
    }

    #endregion

    #region Public Methods

    /// <summary>
    /// Append the specified text to the given TextBox
    /// </summary>
    /// <param name="Message">The message to append</param>
    /// <param name="Control">The TextBox to target</param>
    public static void LogToTextbox(string Message, TextBox Control)
    {
        if (Message.Length > 0)
        {
            Control.AppendText(Message + Environment.NewLine);
            Control.Refresh();
            Application.DoEvents();
        }
    }

    /// <summary>
    /// Setup Logging
    /// </summary>
    /// <param name="Overwrite">If set to true, any existing file will be over-written</param>
    public static void Setup(bool Overwrite = false)
    {
        LoggingConfiguration Config = new LoggingConfiguration();
        FileTarget File = new FileTarget();

        Config.AddTarget("File", File);
        File.Layout = "${message}";
        File.FileName = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), Application.CompanyName, System.Diagnostics.Process.GetCurrentProcess().ProcessName) + ".log";
        File.AutoFlush = true;
        File.KeepFileOpen = false;
        File.ArchiveFileName = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), Application.CompanyName, System.Diagnostics.Process.GetCurrentProcess().ProcessName) + "_{#}.log";
        File.ArchiveNumbering = ArchiveNumberingMode.Rolling;
        File.ArchiveEvery = FileArchivePeriod.Day;
        File.MaxArchiveDays = 31;

        if (Overwrite)
        {
            File.DeleteOldFileOnStartup = true;
        }

        // Create rules
        LoggingRule Rule1 = new LoggingRule("*", LogLevel.Trace, File);

        // Apply rules
        Config.LoggingRules.Add(Rule1);

        // Activate logging
        LogManager.Configuration = Config;

        // Cleanup

        _IsSetup = true;
    }

    /// <summary>
    /// Write the specified message type and string to the logfile, located at %PROGRAMDATA/[Application.CompanyName]
    /// </summary>
    /// <param name="Level">The level of message to write</param>
    /// <param name="Message">The message to write</param>
    public static void Write(LogLevel Level, string Message)
    {
        string Severity;
        string OutputMessage;
        DateTime UtcNow = DateTime.UtcNow;
        DateTime Now = DateTime.Now;
        string UtcDate = UtcNow.ToString("MM-dd-yyyy");
        string UtcTime = UtcNow.ToString("HH:mm:ss.") + UtcNow.Millisecond;
        string Date = Now.ToString("dd-MM-yyyy");
        string Time = Now.ToString("HH:mm:ss.") + UtcNow.Millisecond;
        string TZOffset = TimeZoneInfo.Local.GetUtcOffset(Now).TotalHours.ToString("+000");

        if (!_IsSetup)
        {
            Setup();
        }

        Trace.WriteLine(Message);

        Message = $"{Date} {Time}: {Message}";

        switch (Level.Name)
        {
            default:
                Severity = "0";
                break;
            case "Info":
                Severity = "1";
                break;
            case "Warn":
                Severity = "2";
                break;
            case "Error":
                Severity = "3";
                break;
        }

        // https://adamtheautomator.com/building-logs-for-cmtrace-powershell/

        OutputMessage = $"<![LOG[{Message}]LOG]!><time=\"{UtcTime}{TZOffset}\" date=\"{UtcDate}\" component=\"{GetCurrentMethod()}\" context=\"{Environment.UserName}\" type=\"{Severity}\" thread=\"{Thread.CurrentThread.ManagedThreadId}\" file=\"{System.Diagnostics.Process.GetCurrentProcess().ProcessName}\">";

        // The following can be used as a catch-all

        //try
        //{
        _Log.Log(Level, OutputMessage);
        //}
        //catch (Exception e)
        //{
        //    // If we cannot write to the log file, write to the EventLog instead.
        //    using (EventLog eventLog = new EventLog("Application"))
        //    {
        //        string OutputFolderName = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), Application.CompanyName);
        //        string ExecutableName = System.Diagnostics.Process.GetCurrentProcess().ProcessName;

        //        eventLog.Source = "Application";
        //        eventLog.WriteEntry($"Failed to write to application logfile (in {OutputFolderName}) for {ExecutableName}.  The error was: {e.Message}", EventLogEntryType.Error, 101, 1);
        //    }
        //}
    }

    /// <summary>
    /// Write a error message to the logfile, located at %PROGRAMDATA/[Application.CompanyName]
    /// </summary>
    /// <param name="Message">The message to write</param>
    public static void WriteError(string Message)
    {
        Write(LogLevel.Error, Message);
    }

    /// <summary>
    /// Write an informational message to the logfile, located at %PROGRAMDATA/[Application.CompanyName]
    /// </summary>
    /// <param name="Message">The message to write</param>
    public static void WriteInfo(string Message)
    {
        Write(LogLevel.Info, Message);
    }

    /// <summary>
    /// Write a warning message to the logfile, located at %PROGRAMDATA/[Application.CompanyName]
    /// </summary>
    /// <param name="Message">The message to write</param>
    public static void WriteWarning(string Message)
    {
        Write(LogLevel.Warn, Message);
    }

    #endregion

}

用法:

Logging.Setup();

Logging.WriteInfo("Application startup");

Logging.WriteError($"{DestinationFilename}: Cannot overwrite file.  User advised to delete file manually.  The error was: {ex.Message}");

示例输出:

<![LOG[20-11-2020 13:22:48.626: Application startup]LOG]!><time="05:22:48.626+008" date="11-20-2020" component="Bitberry.Elda.GetLatest.frmMain..ctor" context="DaveR" type="1" thread="1" file="GetLatest">

具体来说,'context="USERNAME"' 部分是您所要求的。


推荐阅读