最近的專案有用到 NLog 來記錄 Log ,在環境建置上處理了一段時間,但是最後還是成功設定好,把 Log 存到資料庫,趁記憶新鮮,趕快紀錄。

使用 NuGet 安裝 NLog

至少要安裝紅色圈起來的 package:

  • NLog
  • NLog.Config
  • NLog.Extended
  • NLog.Web
NLogInstallNLogInstall

新增 Table

在使用 NLog 將 Log 新增到資料庫裡面之前,一定要先有 Table ,我參考官方給的範例,直接新增。
官方文件

SQL
  • sql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
CREATE TABLE [dbo].[NLog] (
[ID] [int] IDENTITY(1,1) NOT NULL,
[MachineName] [nvarchar](200) NULL,
[SiteName] [nvarchar](200) NOT NULL,
[Logged] [datetime] NOT NULL,
[Level] [varchar](5) NOT NULL,
[UserName] [nvarchar](200) NULL,
[Message] [nvarchar](max) NOT NULL,
[Logger] [nvarchar](300) NULL,
[Properties] [nvarchar](max) NULL,
[ServerName] [nvarchar](200) NULL,
[Port] [nvarchar](100) NULL,
[Url] [nvarchar](2000) NULL,
[Https] [bit] NULL,
[ServerAddress] [nvarchar](100) NULL,
[RemoteAddress] [nvarchar](100) NULL,
[Callsite] [nvarchar](300) NULL,
[Exception] [nvarchar](max) NULL,
CONSTRAINT [PK_dbo.Log] PRIMARY KEY CLUSTERED ([ID] 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];

設定 NLog.config

這個步驟最重要,因為設定錯也不知道怎麼 debug XD
XML Elements 說明

  • extensions : 經由擴充功能,來取得一些特定欄位,例如: Web.config的值,或者是 IP 等等….
  • targets : 就是我們 NLog 要寫 Log 的目標,這邊我設定是資料庫
  • target : 資料庫設定的說明,請參考: 資料庫 target 設定
  • rule : 說明什麼層級的訊息要存入 database 這個 target,(共有 6 各層級)
    • Trace
    • Debug
    • Info
    • Warn
    • Error
    • Fatal
NLog.config
  • xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
autoReload="true"
throwExceptions="false"
internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log">

<extensions>
<!-- load NLog.Extended to enable ASP.NET-specific functionality -->
<add assembly="NLog.Extended" />
<add assembly="NLog.Web" />
</extensions>

<targets>
<!-- write log message to database -->
<target name="database" xsi:type="Database">

<dbProvider>System.Data.SqlClient</dbProvider>

<!--帳密驗證-->
<!--<connectionString>server=192.168.0.1;database=TetNlog;user id = abc;password=xxxx</connectionString>-->
<!--Windows驗證-->
<connectionString>(localdb)\ProjectsV12;database=TetNlog;integrated security=True</connectionString>

<commandText>
insert into dbo.Log (
Application, Logged, Level, Message,
Username,
ServerName, Port, Url, Https,
ServerAddress, RemoteAddress,
Logger, CallSite, Exception
) values (
@Application, @Logged, @Level, @Message,
@Username,
@ServerName, @Port, @Url, @Https,
@ServerAddress, @RemoteAddress,
@Logger, @Callsite, @Exception
);
</commandText>

<parameter name="@application" layout="${appsetting:name=AppName:default=Unknown\: set AppName in appSettings}" />
<parameter name="@logged" layout="${date}" />
<parameter name="@level" layout="${level}" />
<parameter name="@message" layout="${message}" />

<parameter name="@username" layout="${identity}" />

<parameter name="@serverName" layout="${aspnet-request:serverVariable=SERVER_NAME}" />
<parameter name="@port" layout="${aspnet-request:serverVariable=SERVER_PORT}" />
<parameter name="@url" layout="${aspnet-request:serverVariable=HTTP_URL}" />
<parameter name="@https" layout="${when:inner=1:when='${aspnet-request:serverVariable=HTTPS}' == 'on'}${when:inner=0:when='${aspnet-request:serverVariable=HTTPS}' != 'on'}" />

<parameter name="@serverAddress" layout="${aspnet-request:serverVariable=LOCAL_ADDR}" />
<parameter name="@remoteAddress" layout="${aspnet-request:serverVariable=REMOTE_ADDR}:${aspnet-request:serverVariable=REMOTE_PORT}" />

<parameter name="@logger" layout="${logger}" />
<parameter name="@callSite" layout="${callsite}" />
<parameter name="@exception" layout="${exception:tostring}" />
</target>
</targets>

<rules>
<logger name="*" minlevel="Trace" writeTo="database" />
</rules>
</nlog>

紀錄 Log 使用方式

一般的使用方式 LogManager.GetCurrentClassLogger(),這個會取得當下 class 的類別存到資料庫

Log 一般使用方式
  • cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MyClass
{
private static Logger logger = LogManager.GetCurrentClassLogger();

public void MyMethod1()
{
logger.Trace("Sample trace message");
logger.Debug("Sample debug message");
logger.Info("Sample informational message");
logger.Warn("Sample warning message");
logger.Error("Sample error message");
logger.Fatal("Sample fatal error message");

// alternatively you can call the Log() method
// and pass log level as the parameter.
logger.Log(LogLevel.Info, "Sample informational message");
}
}

如果是在 BaseClass 的話,則只要呼叫這個方法,則可以取得當下的 class 。

Log BaseClass 使用方式
  • cs
1
2
3
4
5
6
7
8
9
public class BaseApiController  : ApiController
{
public BaseApiController()
{
Logger = LogManager.GetLogger(GetType().FullName);
}

protected Logger Logger { get; private set; }
}

使用 autofac 注入的方式

完成 ILog 介面跟 Logger 實體 class

ILogger 和 Logger
  • cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
public interface ILog
{
void Trace(string format, params object[] args);

void Debug(string format, params object[] args);

void Info(string format, params object[] args);

void Warn(string format, params object[] args);

void Error(string format, params object[] args);

void Error(Exception ex);

void Error(Exception ex, string format, params object[] args);

void Fatal(Exception ex, string format, params object[] args);
}

public class Logger : ILog
{
private readonly Logger _log;

public NLogLogger(Type type)
{
_log = LogManager.GetLogger(type.FullName);
}

public void Trace(string format, params object[] args)
{
Log(LogLevel.Trace, format, args);
}

public void Debug(string format, params object[] args)
{
Log(LogLevel.Debug, format, args);
}

public void Info(string format, params object[] args)
{
Log(LogLevel.Info, format, args);
}

public void Warn(string format, params object[] args)
{
Log(LogLevel.Warn, format, args);
}

public void Error(string format, params object[] args)
{
Log(LogLevel.Error, format, args);
}

public void Error(Exception ex)
{
Log(LogLevel.Error, null, null, ex);
}

public void Error(Exception ex, string format, params object[] args)
{
Log(LogLevel.Error, format, args, ex);
}

public void Fatal(Exception ex, string format, params object[] args)
{
Log(LogLevel.Fatal, format, args, ex);
}

private void Log(LogLevel level, string format, object[] args)
{
_log.Log(typeof(NLogLogger), new LogEventInfo(level, _log.Name, null, format, args));
}

private void Log(LogLevel level, string format, object[] args, Exception ex)
{
_log.Log(typeof(NLogLogger), new LogEventInfo(level, _log.Name, null, format, args, ex));
}
}

新增有 Autofac.Module,註冊 ILog 與 logger ,然後加入 Autofac builder 裡面

Autofac.Module
  • cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public static void Bootstrapper()
{
// web api 的 autofac 設定方式
var builder = new ContainerBuilder();

// Get your HttpConfiguration.
var config = GlobalConfiguration.Configuration;

// Register your Web API controllers.
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

// OPTIONAL: Register the Autofac filter provider.
builder.RegisterWebApiFilterProvider(config);

builder.RegisterModule(new LoggingModule());

var container = builder.Build();

config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}
public class LoggingModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder
.Register((c, p) => new NLogLogger(p.TypedAs<Type>()))
.AsImplementedInterfaces();
}

protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration)
{
registration.Preparing +=
(sender, args) =>
{
var forType = args.Component.Activator.LimitType;

var logParameter = new ResolvedParameter(
(p, c) => p.ParameterType == typeof(ILog),
(p, c) => c.Resolve<ILog>(TypedParameter.From(forType)));

args.Parameters = args.Parameters.Union(new[] { logParameter });
};
}
}

這樣就可以在 Controller 注入,並且還可以取得當下 class 的資訊

參考

官方文件