log4net Action Log 設定

我之前設定網站執行的 Action Log 的一個記錄,有些東西都是拼拼湊湊出來的,並不見得是最佳版本

log4net 設定檔

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
<?xml version="1.0" encoding="utf-8" ?>
<log4net>
<root>
<level value="DEBUG" />
<appender-ref ref="stdout" />
<appender-ref ref="ActionLogRollingFile" />
</root>
<appender name="ActionLogRollingFile" type="log4net.Appender.RollingFileAppender">
<file type="log4net.Util.PatternString" value="action.log" />
<appendToFile value="true" />
<encoding value="utf-8" />
<maxSizeRollBackups value="30" />
<rollingStyle value="Date" />
<datePattern value="'.'yyyy-MM-dd" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="(%date{HH:mm:ss,fff}) [%t] - %u %5property{Duration} %property{StatusCode} %6property{Method} %property{Uri} %m%n " />
</layout>
<filter type="log4net.Filter.LoggerMatchFilter">
<acceptOnMatch value="true" />
<LoggerToMatch value="Sample.Web.Filters.WebApiActionFilter" />
</filter>

<filter type="log4net.Filter.DenyAllFilter" />
</appender>
</log4net>

其中要注意的是

  • log4net.Filter.LoggerMatchFilter: 用來設定特定的 class 記錄才使用這個 Appender
    • acceptOnMatch: 這個設定有點像是 blacklist/whitelist,true 表示有 match 才是,相當於 whitelist
    • LoggerToMatch: 設定要篩選的類別名稱
  • log4net.Filter.DenyAllFilter: 篩選掉其他的 Log,不會在這個 Appender 裡面記錄

C# Filter 程式參考

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
79
80
81
82
83
84
85
86
87
/// <summary>
/// 執行 Web Api 的呼叫記錄
/// </summary>
/// <remarks>
/// 紀錄資料:
///
/// 呼叫時間
/// USER
/// URL
/// METHOD
/// BODY
/// StatusCode
/// Duration
///
/// Logger 的紀錄格式,參考 https://logging.apache.org/log4net/release/sdk/log4net.Layout.PatternLayout.html
/// </remarks>
public class WebApiActionFilter : ActionFilterAttribute
{
private ILog Logger { get; set; }

public WebApiActionFilter()
{
Logger = LogManager.GetLog(GetType());
}

public WebApiActionFilter(ILog logger)
{
Logger = logger;
}

private ThreadLocal<Stopwatch> counter = new ThreadLocal<Stopwatch>(() => new Stopwatch());
public static ThreadLocal<Task<string>> RequestBody = new ThreadLocal<Task<string>>();

public override void OnActionExecuting(HttpActionContext actionContext)
{
RequestBody.Value = actionContext.Request.Content.ReadAsStringAsync();
counter.Value.Start();
}

public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
counter.Value.Stop();
LogActivity(actionExecutedContext.ActionContext, actionExecutedContext.Response);
}

private void LogActivity(HttpActionContext actionContext, HttpResponseMessage response)
{
try
{
var loggerProp = ThreadContext.Properties;
loggerProp["Uri"] = actionContext.Request.RequestUri.PathAndQuery;
loggerProp["Method"] = actionContext.Request.Method.Method;
loggerProp["Duration"] = counter.Value.ElapsedMilliseconds;
loggerProp["StatusCode"] = (int) response.StatusCode;

Logger.Info(RequestBody.Value.Result);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}

/// <summary>
/// 用來擷取傳入的 Request Content Body, 因為沒辦法在 Filter 中取得 Body
/// </summary>
/// <remark>
/// 如果需要抓傳遞資料才需要
/// </remark>
public class LogBodyDelegateHandler : DelegatingHandler
{

protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
if (request.Content != null)
{
var requestText = request.Content.ReadAsStringAsync();

WebApiActionFilter.RequestBody.Value = requestText;
}
// Execute the request, this does not block
var response = base.SendAsync(request, cancellationToken);

return response.Result;
}
}

另外從網路上有查到一篇不錯的文章,http://it.tomtang.idv.tw/2013/12/log4net-best-practice.html
這篇是在說明 log4net 的一些設定建議

以下是他一個常用設定值的參考

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
<?xml version="1.0" encoding="UTF-8"?>
<log4net>
<appender name="ErrorFile" type="log4net.Appender.RollingFileAppender">
<file type="log4net.Util.PatternString" value="C:\applications\logs\%property{appname}\Error-%property{appname}.log" />
<encoding value="utf-8" />
<appendToFile value="true" />
<maximumFileSize value="10000KB" />
<maxSizeRollBackups value="50" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date&#x9;[%thread]&#x9;%level&#x9;%logger&#x9;%property{appname}&#x9;- %message%newline" />
</layout>
<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value="ERROR" />
<levelMax value="FATAL" />
</filter>
<filter type="log4net.Filter.DenyAllFilter" />
</appender>
<appender name="WarnFile" type="log4net.Appender.RollingFileAppender">
<file type="log4net.Util.PatternString" value="C:\applications\logs\%property{appname}\Warn-%property{appname}.log" />
<encoding value="utf-8" />
<appendToFile value="true" />
<maximumFileSize value="10000KB" />
<maxSizeRollBackups value="50" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date&#x9;[%thread]&#x9;%level&#x9;%logger&#x9;%property{appname}&#x9;- %message%newline" />
</layout>
<filter type="log4net.Filter.LevelMatchFilter">
<param name="LevelToMatch" value="WARN" />
</filter>
<filter type="log4net.Filter.DenyAllFilter" />
</appender>
<appender name="InfoFile" type="log4net.Appender.RollingFileAppender">
<file type="log4net.Util.PatternString" value="C:\applications\logs\%property{appname}\Info-%property{appname}.log" />
<encoding value="utf-8" />
<appendToFile value="true" />
<maximumFileSize value="10000KB" />
<maxSizeRollBackups value="50" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date&#x9;[%thread]&#x9;%level&#x9;%logger&#x9;%property{appname}&#x9;- %message%newline" />
</layout>
<filter type="log4net.Filter.LevelMatchFilter">
<LevelToMatch value="INFO" />
</filter>
<filter type="log4net.Filter.DenyAllFilter" />
</appender>
<appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
<file type="log4net.Util.PatternString" value="C:\applications\logs\%property{appname}\ALL-%property{appname}.log" />
<encoding value="utf-8" />
<appendToFile value="true" />
<maximumFileSize value="10000KB" />
<maxSizeRollBackups value="50" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date&#x9;[%thread]&#x9;%level&#x9;%logger&#x9;%property{appname}&#x9;- %message%newline" />
</layout>
</appender>
<appender name="debugger" type="log4net.Appender.OutputDebugStringAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date&#x9;[%thread]&#x9;%level&#x9;%logger&#x9;%property{appname}&#x9;- %message%newline" />
</layout>
</appender>
<root additivity="true">
<level value="ALL" />
<appender-ref ref="debugger" />
<appender-ref ref="RollingFile" />
<appender-ref ref="InfoFile" />
<appender-ref ref="WarnFile" />
<appender-ref ref="ErrorFile" />
</root>
<logger name=「doway.sample.helloworld」>
<level value="WARN" />
</logger>
</log4net>

參考