一个简单的封装 .net的日志功能
因为项目需要 ,经常自己写一点工具类
下面就是一个简单的日志类,支持多线程,不锁定文件,需要CLFS支持,性能非常好
0警告 0stylecop
代码
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Logger.cs" company="Clover">
// Copyright (c) Clover, All Rights Reserved.
// </copyright>
// <summary>
// Provides methods to write application logs based on a Common Log File System. This class cannot be inherited.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace Clover
{
#region Using directives
using System;
using System.Configuration;
using System.Globalization;
using System.IO;
using System.IO.Log;
using System.Text;
#endregion
/// <summary>
/// Encapsulates a type to write application logs based on a Common Log File System. This class cannot be inherited.
/// </summary>
public sealed class Logger : IDisposable
{
#region Constants and Fields
/// <summary>
/// The key of the configuration for base name.
/// </summary>
public const string BaseNameKey = "Clover.LogBaseName";
/// <summary>
/// The key of the configuration for base directory.
/// </summary>
public const string BasePathKey = "Clover.LogBasePath";
/// <summary>
/// The key of the configuration for extent size.
/// </summary>
public const string ExtentSizeKey = "Clover.ExtentSize";
/// <summary>
/// The default size for each log extent.
/// </summary>
private const int DefaultExtentSize = 10 * 1024 * 1024;
/// <summary>
/// The log record sequence object to write the log entries.
/// </summary>
private readonly LogRecordSequence _sequence;
/// <summary>
/// The log store to hold the log record sequence object.
/// </summary>
private readonly LogStore _store;
#endregion
#region Constructors and Destructors
/// <summary>
/// Initializes a new instance of the <see cref="Logger"/> class.
/// </summary>
/// <param name="basePath">
/// The base path where the log files are resided.
/// </param>
/// <param name="baseName">
/// The base name of the log files.
/// </param>
/// <param name="extentSize">
/// Size of each log extent in bytes.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="basePath"/> is null, or <paramref name="baseName"/> is null.
/// </exception>
public Logger(string basePath, string baseName, int extentSize)
{
if (string.IsNullOrEmpty(basePath))
{
throw new ArgumentNullException("basePath");
}
if (string.IsNullOrEmpty(baseName))
{
throw new ArgumentNullException("baseName");
}
// If the base path doesn't exist, create it.
if (!Directory.Exists(basePath))
{
Directory.CreateDirectory(basePath);
}
string fullPath = Path.Combine(basePath, baseName);
this._store = new LogStore(fullPath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
this._sequence = new LogRecordSequence(this._store);
this._sequence.RetryAppend = true;
if (this._store.Extents.Count == 0)
{
this._store.Extents.Add(fullPath, extentSize);
}
}
/// <summary>
/// Initializes a new instance of the <see cref="Logger"/> class.
/// </summary>
/// <param name="basePath">
/// The base path where the log files are resided.
/// </param>
/// <param name="baseName">
/// The base name of the log files.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="basePath"/> is null, or <paramref name="baseName"/> is null.
/// </exception>
public Logger(string basePath, string baseName)
: this(basePath, baseName, Logger.DefaultExtentSize)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Logger"/> class by reading the arguments from the application
/// configuration file.
/// </summary>
/// <exception cref="ArgumentNullException">
/// One or more configuration values are not found in the application's configuration file.
/// </exception>
public Logger()
: this(
ConfigurationManager.AppSettings[Logger.BasePathKey],
ConfigurationManager.AppSettings[Logger.BaseNameKey],
Convert.ToInt32(ConfigurationManager.AppSettings[Logger.DefaultExtentSize]))
{
}
#endregion
#region Public Methods
/// <summary>
/// Writes the specified entry to the log store with format arguments.
/// </summary>
/// <param name="entry">
/// The entry to write.
/// </param>
/// <param name="args">
/// The arguments to format the entry.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="entry"/> is null.
/// </exception>
public void WriteEntry(string entry, params object[] args)
{
if (string.IsNullOrEmpty(entry))
{
throw new ArgumentNullException("entry");
}
entry = string.Format(CultureInfo.CurrentCulture, entry, args);
this.WriteEntry(entry);
}
/// <summary>
/// Writes the specified entry to the log store with format arguments.
/// </summary>
/// <param name="entry">
/// The entry to write.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="entry"/> is null.
/// </exception>
public void WriteEntry(string entry)
{
if (string.IsNullOrEmpty(entry))
{
throw new ArgumentNullException("entry");
}
byte[] bytes = Encoding.Unicode.GetBytes(entry);
this._sequence.Append(new ArraySegment<byte>(bytes), SequenceNumber.Invalid, SequenceNumber.Invalid, RecordAppendOptions.None);
}
#endregion
#region Implemented Interfaces
#region IDisposable
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
#endregion
#region Methods
/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing">
/// <c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.
/// </param>
private void Dispose(bool disposing)
{
if (disposing)
{
if (this._sequence != null)
{
this._sequence.Dispose();
}
if (this._store != null)
{
this._store.Dispose();
}
}
}
#endregion
}
}
以下是单元测试代码:
代码
#region Using directives
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Threading;
#endregion
/// <summary>
/// Contains tests for the Log class.
/// </summary>
[TestClass]
public class LoggerTest : UnitTestBase
{
/// <summary>
/// The base path for log test.
/// </summary>
private readonly string _basePath = Environment.ExpandEnvironmentVariables(@"%UserProfile%\Documents\LogTest");
/// <summary>
/// The base name for log test.
/// </summary>
private const string BaseName = "MyLog";
#region Public Methods
/// <summary>
/// Tests the Constructor.
/// </summary>
[TestMethod]
public void ConstructorTest()
{
// Scenario 1: Create a normal Logger using parameterized constructor.
Logger logger = new Logger(this._basePath, BaseName);
Assert.IsNotNull(logger);
// Scenario 2: Create a normal logger using default configuration.
try
{
new Logger();
Assert.Fail("Expect ArgumentNullException or ArgumentOutOfRangeException");
}
catch (ArgumentOutOfRangeException)
{
Assert.IsTrue(true);
}
catch (ArgumentNullException)
{
Assert.IsTrue(true);
}
finally
{
logger.Dispose();
}
}
/// <summary>
/// Tests the WriteEntry method.
/// </summary>
[TestMethod]
public void WriteEntryTest()
{
// Scenario 1: Write one entry.
string content = "A quick brown fox jumps over the lazy dog";
using (Logger logger = new Logger(this._basePath, BaseName))
{
logger.WriteEntry(content);
Assert.IsTrue(true);
// Scenario 2: Write 1,000,000 entries at a time.
for (int i = 0; i < 1000000; i++)
{
logger.WriteEntry(content);
}
// Scenario 3: Write one entry with format.
content = "{0} - {1}: {2}";
logger.WriteEntry(content, 1, 2, 3);
}
}
#endregion
}