[研究]ASP.NET WebForm + SQL Server 2019 網站的AP Log資料表推薦?
2025-11-22
突然想到,問問看 ChatGPT 的 APLog 建議。
開發一個 ASP.NET WebForm + SQL Server 2019 網站,請推薦 DB 中 Log table 有哪欄位,欄位型態(含長度、是否預設值、預設值、是否允許 NULL、、、) 分別是是甚麼 ? 表格呈現 ?
ChatGPT
✅ 一、Log Table 欄位建議(最推薦版本)
| 欄位名稱 | 型態 | 長度 | NULL? | 預設值 | 說明 |
|---|---|---|---|---|---|
| LogID | BIGINT |
— | NOT NULL | IDENTITY(1,1) | Primary Key,自動遞增 |
| LogTime | datetime2(3) |
— | NOT NULL | SYSDATETIME() |
建議記到毫秒(.000~.999) |
| Level | varchar(20) |
20 | NOT NULL | 'INFO' |
Log 等級,例如 INFO / WARN / ERROR |
| UserID | varchar(50) |
50 | NULL | NULL | 來源使用者代號(可 NULL) |
| ClientIP | varchar(45) |
45 | NULL | NULL | IPv4/IPv6 都可記錄 |
| PageName | varchar(200) |
200 | NULL | NULL | 記錄 ASPX 頁面(或 API 名稱) |
| Action | varchar(200) |
200 | NULL | NULL | 使用者動作、事件名 |
| Message | nvarchar(max) |
— | NULL | NULL | 主訊息、簡述 |
| Detail | nvarchar(max) |
— | NULL | NULL | 完整訊息(如 Exception.ToString) |
| MachineName | varchar(100) |
100 | NOT NULL | HOST_NAME() |
伺服器名稱 |
| AppName | varchar(50) |
50 | NOT NULL | 'WebFormSite' |
系統識別名(可改) |
| TraceID | varchar(50) |
50 | NULL | NULL | 用於跨 API、跨系統追蹤 |
✔ 為何選用 datetime2(3)?
datetime2(3)
- 精度:毫秒(需要 Web Log 精度通常到此即可)
- 容量:7 bytes
- 預設值可用:SYSDATETIME()(datetime2 版)
- 最新 SQL Server 推薦使用 datetime2 取代 datetime
✔ 為何 Level、PageName、Action 建議 varchar?
因為:
- WebForm 系統場景中無需 Unicode
- varchar 資料更小、更省 index 空間
- 查詢效能比 nvarchar 好
- Message、Detail 則可能含中文與 Exception,所以使用 nvarchar(max)。
✔ 有沒有必要建立索引?
建議以下 Index:
- LogTime(降序)
- Level + LogTime
- UserID + LogTime
- TraceID(若跨系統必須)
✅ 二、SQL 建議 DDL
CREATE TABLE dbo.Logs
(
LogID BIGINT IDENTITY(1,1) NOT NULL PRIMARY KEY,
LogTime datetime2(3) NOT NULL DEFAULT SYSDATETIME(),
Level varchar(20) NOT NULL DEFAULT ('INFO'),
UserID varchar(50) NULL,
ClientIP varchar(45) NULL,
PageName varchar(200) NULL,
Action varchar(200) NULL,
Message nvarchar(max) NULL,
Detail nvarchar(max) NULL,
MachineName varchar(100) NOT NULL DEFAULT HOST_NAME(),
AppName varchar(50) NOT NULL DEFAULT ('WebFormSite'),
TraceID varchar(50) NULL
);
|
▶ 三、在 ASP.NET WebForm GridView 的呈現建議
GridView 最常顯示欄位:
| 顯示 | 不顯示(或隱藏) |
|---|---|
| LogTime | LogID |
| Level | MachineName |
| UserID | AppName |
| ClientIP | TraceID |
| PageName | Detail(按按鈕展開) |
| Action | |
| Message |
✔ GridView 最佳呈現(程式碼示例)
.aspx
<asp:GridView ID="gvLog" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:BoundField DataField="LogTime" HeaderText="時間"
DataFormatString="{0:yyyy-MM-dd HH:mm:ss.fff}" />
<asp:BoundField DataField="Level" HeaderText="層級" />
<asp:BoundField DataField="UserID" HeaderText="使用者" />
<asp:BoundField DataField="ClientIP" HeaderText="IP" />
<asp:BoundField DataField="PageName" HeaderText="頁面" />
<asp:BoundField DataField="Action" HeaderText="動作" />
<asp:BoundField DataField="Message" HeaderText="訊息" />
<!-- Detail 隱藏,改成按鈕彈出 -->
<asp:TemplateField HeaderText="詳細">
<ItemTemplate>
<asp:Button runat="server" Text="查看"
CommandName="ViewDetail"
CommandArgument='<%# Eval("LogID") %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
|
📌 四、如果需要寫入程式碼(C# 範例)
Default.aspx
public static void WriteLog(string level, string user, string page, string action, string msg, string detail)
{
using (SqlConnection conn = new SqlConnection(connStr))
using (SqlCommand cmd = new SqlCommand(@"
INSERT INTO Logs (Level, UserID, PageName, Action, Message, Detail, ClientIP)
VALUES (@Level, @UserID, @PageName, @Action, @Message, @Detail, @IP)
", conn))
{
cmd.Parameters.AddWithValue("@Level", level);
cmd.Parameters.AddWithValue("@UserID", (object)user ?? DBNull.Value);
cmd.Parameters.AddWithValue("@PageName", page);
cmd.Parameters.AddWithValue("@Action", action);
cmd.Parameters.AddWithValue("@Message", msg);
cmd.Parameters.AddWithValue("@Detail", (object)detail ?? DBNull.Value);
cmd.Parameters.AddWithValue("@IP", HttpContext.Current.Request.UserHostAddress);
conn.Open();
cmd.ExecuteNonQuery();
}
}
|
********************************************************************************
這是 ASP.NET WebForm 繁體中文網站,為何是否允許NULL 建議用 NULL ? 某些 "預設值" 建議用 NULL,而不是 N'' 空字串 ? 型態建議用 varchar,而不是 nvarchar ?
ChatGPT
✅ 為何 大部分欄位建議允許 NULL?
✔ 1. Log 本質是「紀錄事件」,不是所有欄位每次都會有資料
👉 因此 強迫 NOT NULL + 空字串 會讓資料:難以判斷「真的沒有」 vs. 「空字串」
✔ 2. 空字串與 NULL 的語意完全不同
狀態 意義
NULL 這項資料「不存在」
''(空字串) 這項資料存在,只是內容是空
📌 Log 中最重要的是正確語意,因此推薦:
| 狀態 | 意義 |
|---|---|
| NULL | 這項資料「不存在」 |
| ''(空字串) | 這項資料存在,只是內容是空 |
❌ 若全部欄位 NOT NULL + 空字串,會變成問題
例如你要查「沒有 UserID 的 Log」:
你必須用:
WHERE UserID IS NULL OR UserID = ''
這會變得很亂,也容易寫錯。
🟦 結論
在 Log Table 中,多數欄位允許 NULL 才是真正國際慣例,也符合語意正確性。
✅ 為何某些欄位「預設值」建議用 NULL,而不是 N'' 空字串?
✔ 1. 預設值 NULL 表示「沒有提供」
例如呼叫 Log 方法時沒有傳 Action,你希望 DB 記錄「無動作」,不是「空白動作」。
✔ 2. 空字串會破壞查詢語意
空字串實際上是一個「值」,但它不代表不存在。
✔ 3. 節省儲存空間
NULL 在 SQL Server 的儲存是 比空字串更省空間的,因為儲存格式不同。
✔ 4. 避免 Query Index 汙染
使用空字串會造成 B-Tree index 多一個不必要的 entry。
✔ 為何欄位型態建議用 varchar 而不是 nvarchar?
即使你的網站是 繁體中文網站,仍然有三個核心原因:
🎯 核心原因 1:Log 中大部分欄位是英文,不需 Unicode。
🎯 核心原因 2:varchar 效能與空間比 nvarchar 更好
在 SQL Server 中:
varchar 為 1 byte / 字元
nvarchar 為 2 bytes / 字元
Log 可能幾百萬筆,差異大:
| 型態 | 儲存空間 | 意義 |
|---|---|---|
| varchar(50) | 50 bytes | 較小、較快 |
| nvarchar(50) | 100 bytes | 大 2 倍,index 也大 2 倍 |
Log table 通常很大,使用 varchar 可節省大量空間並提升查詢速度。
🎯 核心原因 3:繁體中文只會出現在 Message / Detail
這兩個欄位我已建議用:
nvarchar(max)
nvarchar(max)
這些欄位確實會包含中文、例外訊息、堆疊資訊。
但其他用來辨識頁面、欄位、行為的欄位多為英文,varchar 完全足夠。
********************************************************************************
查詢條件,TextBox1 為完全比對,空白時查全部;TextBox2 為完全比對,空白時查全部,否則使用 LIKE,code 盡量不寫 aspx.cs 中,寫在 .aspx 中就好
.aspx
<asp:TextBox ID="TextBox1" runat="server" />
<asp:TextBox ID="TextBox2" runat="server" />
<asp:Button ID="btnSearch" runat="server" Text="查詢" />
<asp:SqlDataSource ID="SqlDataSource1" runat="server"
ConnectionString="<%$ ConnectionStrings:MyConn %>"
SelectCommand="
SELECT *
FROM LogTable
WHERE
-- TextBox1:空白查全部,有值則完全比對
(@p1 = '' OR Column1 = @p1)
AND
-- TextBox2:空白查全部,有值則 LIKE 模糊比對
(@p2 = '' OR ISNULL(Column2, '') LIKE @like_p2)
">
<SelectParameters>
<asp:ControlParameter Name="p1" ControlID="TextBox1" PropertyName="Text" />
<asp:ControlParameter Name="p2" ControlID="TextBox2" PropertyName="Text" />
<!-- 給 TextBox2 做 LIKE 用 -->
<asp:ControlParameter Name="like_p2"
ControlID="TextBox2"
PropertyName="Text"
ConvertEmptyStringToNull="false"
DefaultValue="" />
</SelectParameters>
<!-- 調整參數:在 Selecting 事件中加入 '%'(無需寫 aspx.cs!) -->
<SelectParameters>
</SelectParameters>
</asp:SqlDataSource>
<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" />
|
.aspx
<asp:SqlDataSource ID="SqlDataSource1" runat="server"
OnSelecting="SqlDataSource1_Selecting"
...
>
|
.aspx
<script runat="server">
protected void SqlDataSource1_Selecting(object sender, SqlDataSourceSelectingEventArgs e)
{
string p2 = (string)e.Command.Parameters["@p2"].Value;
// TextBox2 空白:查全部 → LIKE 條件會被 (@p2 = '') 篩掉
if (string.IsNullOrEmpty(p2))
{
e.Command.Parameters["@like_p2"].Value = "";
}
else
{
// LIKE '%keyword%'
e.Command.Parameters["@like_p2"].Value = "%" + p2 + "%";
}
}
</script>
|
(完)
沒有留言:
張貼留言