小さなC#winformsアプリケーションを作成しました。追加機能として、何らかの形式のエラーログインを追加することを検討していました。誰もこれについて良い方法を提案していますか?これは以前のプロジェクトへの追加を検討したことのない機能なので、より多くの経験を持つ開発者からの提案を受け付けています。
特定のテキストファイル、またはデータベーステーブルに例外を書き込むという行に沿って何かを検討していました。これは、数か月間使用され、その後、より大きな製品が完成すると破棄されるアプリケーションです。
ロギングのニーズは単純なので、外部ライブラリについてはあまり掘り下げません。
.NET Frameworkには、名前空間System.Diagnosticsにこの機能が既に付属しているため、 Trace クラスの下でメソッドを呼び出すだけで、必要なすべてのログを書き込むことができます。
Trace.TraceInformation("Your Information");
Trace.TraceError("Your Error");
Trace.TraceWarning("Your Warning");
そして、app.configファイルでニーズに合ったすべてのトレースリスナーを構成します。
<configuration>
// other config
<system.diagnostics>
<trace autoflush="true" indentsize="4">
<listeners>
<add name="consoleListener" type="System.Diagnostics.ConsoleTraceListener"/>
<add name="textWriterListener" type="System.Diagnostics.TextWriterTraceListener" initializeData="YourLogFile.txt"/>
<add name="eventLogListener" type="System.Diagnostics.EventLogTraceListener" initializeData="YourEventLogSource" />
<remove name="Default"/>
</listeners>
</trace>
</system.diagnostics>
// other config
</configuration>
または、必要に応じて、構成ファイルに依存せずに、アプリケーションでリスナーを構成することもできます。
Trace.Listeners.Add(new TextWriterTraceListener("MyTextFile.log"));
テキストログが正しく機能するように、Trace.AutoFlushプロパティを必ずtrueに設定してください。
私の意見では、最適な解決策はNLogを使用することです。 http://nlog-project.org/
NuGetから構成パッケージをインストールするだけです: http://www.nuget.org/packages/NLog.Config/ .
次に、あなたのコードで必要なのは:
// A logger member field:
private readonly Logger logger = LogManager.GetCurrentClassLogger(); // creates a logger using the class name
// use it:
logger.Info(...);
logger.Error(...);
// and also:
logger.ErrorException("text", ex); // which will log the stack trace.
取得した設定ファイルで、必要なセクションのコメントを外す必要があります。
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!--
See http://nlog-project.org/wiki/Configuration_file
for information on customizing logging rules and outputs.
-->
<targets>
<!-- add your targets here -->
<!-- UNCOMMENT THIS!
<target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log"
layout="${longdate} ${uppercase:${level}} ${message}" />
-->
</targets>
<rules>
<!-- add your logging rules here -->
<!-- UNCOMMENT THIS!
<logger name="*" minlevel="Trace" writeTo="f" />
-->
</rules>
</nlog>
nlog.config
ファイルのプロパティを編集して
Copy to Output Directory: Copy always
SimpleLog を使用できます。
シンプルでありながら堅牢で強力な1クラスロギングソリューションであり、理解しやすく、統合しやすく、使いやすいです。 log4Netのセットアップとカスタマイズに数日を費やす必要はありません。そのクラスを使えば、数分で完了です。
現在はファイルにログを記録していますが、データベースにログを記録するように簡単にカスタマイズできる必要があります。
まあlog4netはレンガのように動作します。設定するのは少し難しいかもしれませんが、その価値はあります。また、これらのログファイルなどのファイルロックを設定することもできます。
Log.csというクラスを作成します。LinqTo SQlを使用してデータベースに保存します
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
public static partial class Log
{
/// <summary>
/// Saves the exception details to ErrorLogging db with Low Priority
/// </summary>
/// <param name="ex">The exception.</param>
public static void Save(this Exception ex)
{
Save(ex, ImpactLevel.Low, "");
}
/// <summary>
/// Saves the exception details to ErrorLogging db with specified ImpactLevel
/// </summary>
/// <param name="ex">The exception.</param>
/// <param name="impactLevel">The Impact level.</param>
public static void Save(this Exception ex, ImpactLevel impactLevel)
{
Save(ex, impactLevel,"");
}
/// <summary>
/// Saves the exception details to ErrorLogging db with specified ImpactLevel and user message
/// </summary>
/// <param name="ex">The exception</param>
/// <param name="impactLevel">The impact level.</param>
/// <param name="errorDescription">The error Description.</param>
public static void Save(this Exception ex, ImpactLevel impactLevel, string errorDescription)
{
using (var db = new ErrorLoggingDataContext())
{
Log log = new Log();
if (errorDescription != null && errorDescription != "")
{
log.ErrorShortDescription = errorDescription;
}
log.ExceptionType = ex.GetType().FullName;
var stackTrace = new StackTrace(ex, true);
var allFrames = stackTrace.GetFrames().ToList();
foreach (var frame in allFrames)
{
log.FileName = frame.GetFileName();
log.LineNumber = frame.GetFileLineNumber();
var method = frame.GetMethod();
log.MethodName = method.Name;
log.ClassName = frame.GetMethod().DeclaringType.ToString();
}
log.ImpactLevel = impactLevel.ToString();
try
{
log.ApplicationName = Assembly.GetCallingAssembly().GetName().Name;
}
catch
{
log.ApplicationName = "";
}
log.ErrorMessage = ex.Message;
log.StackTrace = ex.StackTrace;
if (ex.InnerException != null)
{
log.InnerException = ex.InnerException.ToString();
log.InnerExceptionMessage = ex.InnerException.Message;
}
log.IpAddress = ""; //get the ip address
if (System.Diagnostics.Debugger.IsAttached)
{
log.IsProduction = false;
}
try
{
db.Logs.InsertOnSubmit(log);
db.SubmitChanges();
}
catch (Exception eex)
{
}
}
}
}
次の表を作成します
USE [database Name]
GO
/****** Object: Table [dbo].[Log] Script Date: 9/27/2016 11:52:32 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Log](
[LogId] [INT] IDENTITY(1,1) NOT NULL,
[ErrorDate] [DATETIME] NOT NULL CONSTRAINT [DF_Log_Date] DEFAULT (GETDATE()),
[ErrorShortDescription] [VARCHAR](1000) NULL,
[ExceptionType] [VARCHAR](255) NULL,
[FileName] [VARCHAR](1000) NULL,
[LineNumber] [INT] NULL,
[MethodName] [VARCHAR](255) NULL,
[ClassName] [VARCHAR](150) NULL,
[ImpactLevel] [VARCHAR](50) NOT NULL,
[ApplicationName] [VARCHAR](255) NULL,
[ErrorMessage] [VARCHAR](4000) NULL,
[StackTrace] [VARCHAR](MAX) NULL,
[InnerException] [VARCHAR](2000) NULL,
[InnerExceptionMessage] [VARCHAR](2000) NULL,
[IpAddress] [VARCHAR](150) NULL,
[IsProduction] [BIT] NOT NULL CONSTRAINT [DF_Log_IsProduction] DEFAULT ((1)),
[LastModified] [DATETIME] NOT NULL CONSTRAINT [DF_Log_LastModified] DEFAULT (GETDATE()),
CONSTRAINT [PK_Log] PRIMARY KEY CLUSTERED
(
[LogId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'This table holds all the exceptions.
ErrorData = when error happened
,[ErrorShortDescription] == short desc about the error entered by the developers
,[FileName] = file where error happened full path
,[LineNumber] = line number where code failed
,[MethodName] = method name where exception happened
,[ClassName] = class where exception happened
,[ImpactLevel] = high, medium, low
,[ApplicationName] = name of the application where error came from
,[ErrorMessage] = exception error messge
,[StackTrace] = C# stack trace
,[InnerException] = inner exception of strack trace
,[InnerExceptionMessage] = inner message
,[IpAddress]
,[IsProduction]' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'Log'
GO
影響レベルは基本的に列挙です
public enum ImpactLevel
{
High = 0,
Medium = 1,
Low = 2,
}
次のように使用できます
try
{
}
catch(Exception ex)
{
//this will save the exception details and mark exception as low priority
ex.Save();
}
try
{
}
catch(Exception ex)
{
//this will save the exception details with priority you define: High, Medium,Low
ex.Save(ImpactLevel.Medium);
}
try
{
}
catch(Exception ex)
{
//this will save the exception details with priority you define: High, Medium,Low
ex.Save(ImpactLevel.Medium, "You can enter an details you want here ");
}
Log4netの例を次に示します。
次のプログラムを作成します。
using System.Threading.Tasks;
using log4net;
using System.Text;
using System.CollectionsGeneric;
using System;
namespace Log4NetTest
{
class Program
{
private static readonly ILog _logger = LogManager.GetLogger("testApp.LoggingExample");
static void Main(string[] args)
{
// Configure from App.config. This is marked as obsolete so you can also add config into separate config file
// and use log4net.Config.XmlConfigurator method to configure from xml file.
log4net.Config.DOMConfigurator.Configure();
_logger.Debug("Shows only at debug");
_logger.Warn("Shows only at warn");
_logger.Error("Shows only at error");
Console.ReadKey();
}
}
}
App.configを次のように変更します。
<!-- language: xml -->
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<log4net debug="false">
<appender name="LogFileAppender" type="log4net.Appender.FileAppender,log4net" >
<param name="File" value="myLog.log" />
<param name="AppendToFile" value="true" />
<layout type="log4net.Layout.PatternLayout,log4net">
<param name="ConversionPattern" value="%date [%thread] %-5level %logger %ndc - %message%newline" />
</layout>
</appender>
<root>
<priority value="ALL" />
<appender-ref ref="LogFileAppender" />
</root>
<category name="testApp.LoggingExample">
<priority value="ALL" />
</category>
</log4net>
</configuration>
5.アプリケーションを実行すると、bin\Debugフォルダーから次のファイルが見つかります。
2013-12-13 13:27:27,252 [8] DEBUG testApp.LoggingExample (null) - Shows only at debug
2013-12-13 13:27:27,280 [8] WARN testApp.LoggingExample (null) - Shows only at warn
2013-12-13 13:27:27,282 [8] ERROR testApp.LoggingExample (null) - Shows only at error
ここで提案を読んだ後、私は次のものを使用することになりました:
private void LogSystemError(string message)
{
EventLog.WriteEntry("YourAppName", message, EventLogEntryType.Error);
}
EventLogクラスは、System.Diagnosticsを使用して利用できます。
複数のスレッドロギングエラーの同時発生の問題、ファイルの場所とアクセスセキュリティ、およびファイルが大きくなりすぎる可能性がある問題を回避するために、ファイルにログインするオプション(「yourLogFile.txt」など)を回避しました。
例外エラーをテキストファイルに書き出すだけです。 テキストファイルに書き込む ただし、作成するファイルをuserdataまたはappdataディレクトリに配置することをお勧めします。そのため、アクセス許可に苦労する必要はありません。
これは数か月間のみ必要であり、破棄されるため、DBを使いすぎる理由はありません。シンプルなテキストファイルで十分です。