統操作日志在軟件開發和運維過程中起著至關重要的作用。通過記錄系統在運行時的關鍵操作和事件,操作日志能幫助開發者監控系統狀態、診斷問題、追蹤錯誤,并為系統的安全性和穩定性提供保障。在C#中,我們可以利用多種方法來編寫系統操作日志,本文將詳細介紹這一過程,并提供實用的示例代碼。
一、系統操作日志的重要性
系統操作日志記錄了軟件或系統在運行時的各種活動,包括但不限于用戶登錄、數據修改、異常發生等關鍵事件。這些日志對于后續的故障排查、系統優化、安全審計等方面都具有極高的價值。通過查看和分析日志,開發人員可以快速定位并解決問題,從而提高系統的可靠性和性能。
二、在C#中編寫系統操作日志
在C#中,我們可以使用內置的System.Diagnostics
命名空間中的Trace
和Debug
類來記錄日志,也可以使用第三方的日志庫,如NLog、log4net等。以下是一個使用Trace
類的基本示例:
System.Diagnostics
命名空間。using System.Diagnostics;
配置監聽器:在應用程序的配置文件(如App.config或Web.config)中,你可以配置Trace
類的監聽器,以決定將日志信息輸出到哪里,比如文件、控制臺或Windows事件查看器等。
編寫日志:在你的代碼中,你可以使用Trace.WriteLine
方法來記錄日志。
Trace.WriteLine("This is a trace message.");
#if DEBUG
Trace.WriteLine("Debug message.");
#endif
三、示例代碼
以下是一個簡單的示例,展示了如何在C#中使用Trace
類記錄系統操作日志:
using System;
using System.Diagnostics;
namespace LoggingExample
{
class Program
{
static void Main(string[] args)
{
// 配置Trace監聽器(通常在配置文件中完成)
Trace.Listeners.Add(new TextWriterTraceListener("log.txt"));
Trace.AutoFlush=true;
// 記錄操作日志
Trace.WriteLine("System started at " + DateTime.Now);
// 模擬一些系統操作
PerformSomeOperation();
// 記錄操作完成日志
Trace.WriteLine("Operation completed successfully.");
}
static void PerformSomeOperation()
{
Trace.WriteLine("Performing some operation...");
// 執行具體操作的代碼...
}
}
}
在這個示例中,我們創建了一個TextWriterTraceListener
來將日志信息寫入名為"log.txt"的文件中。然后,在程序的關鍵點,我們使用Trace.WriteLine
方法來記錄日志。
四、日志編寫的最佳實踐
Python 項目中,使用打印語句和堆棧跟蹤很容易跟蹤代碼進度和調試。但是,日志記錄提供了使用時間戳、文件名、代碼行數等進行跟蹤和調試的附加功能,區分不同類型的打印語句,甚至可以選擇將打印語句保存到日志文件或其他位置,而不是只能在控制臺或命令行上查看。
本文將介紹日志記錄的組件,如何在文件中執行日志記錄,以及如何使用配置以可重用和可擴展的方式跨多個文件執行日志記錄。
在深入研究代碼實現之前,一些組件對于有效使用日志記錄是不可或缺的。
建議將日志保存到日志文件中,而不是在控制臺或命令行上查看它們,因為一旦控制臺或終端關閉,信息就會消失。我們可以指定要存儲日志的項目的相對路徑。
您還可以定義保存模式。默認情況下,日志以追加模式保存,但可以更改為寫入模式以覆蓋以前的日志。
根據所跟蹤事件的任務和嚴重性,有 5 種日志記錄級別。按照嚴重程度遞增的順序,
有疑問時,我總是INFO在正常操作時使用水平儀,WARN或者ERROR在遇到小問題和主要問題時使用水平儀。
默認情況下,日志級別為WARN,表示低于該級別的日志級別,即DEBUG和INFO,除非更改默認日志級別,否則不會記錄。
可以設置日志格式,并將此格式應用于所有日志條目——這意味著您不必手動將相同的格式標準化到每個日志調用!默認情況下,日志條目遵循格式levelname:name:message,例如,DEBUG:root:This is a log entry但可以自定義以包含更多信息,
要指定格式,我們使用"%(component)s"約定,例如"%(levelname)s". 例如,默認格式表示為"%(levelname)s:%(name)s:%(message)s". 這將在下一節中詳細說明。
在了解了日志記錄組件之后,我們準備記錄一些信息!日志記錄可以這樣完成,
import logging
logging.basicConfig(
filename="output.log",
filemode="w",
level=logging.DEBUG,
format="%(asctime)s:%(levelname)s:%(message)s",
datefmt="%Y-%m-%d %I:%M:%S%p",
)
logging.debug("Log with debug-level severity")
logging.info("Log with info-level severity")
logging.warning("Log with warning-level severity")
logging.error("Log with error-level severity")
logging.critical("Log with error-level severity")
try:
raise Exception("Throw exception")
except Exception as e:
logging.error("Exception occurred", exc_info=True)
在上面的例子中,我們使用basicConfig來指定日志配置,
指定配置后,我們可以在代碼中插入日志調用等logging.info()來執行日志記錄。默認情況下,只會記錄日志消息,但我們也可以設置exc_info=True捕獲堆棧跟蹤,如第 19 行所示。
話雖如此,使用basicConfig實現日志記錄需要在不同的文件中定義配置,這會導致重復的代碼。在下一節中,我們將使用配置文件以可重用和可擴展的方式實現日志記錄。
對于日志的高級用法,我們可以在配置文件中定義日志配置,使其可重復用于跨多個 Python 文件進行日志記錄。如果以這種方式實現日志記錄,則可以完成更多項目和自定義,并且它建立在上一節中的基本組件之上。
記錄器公開應用程序代碼直接使用的接口
記錄器對應name于日志條目中的屬性。默認情況下root選擇記錄器,其設置(例如保存路徑、保存模式、格式等)由處理程序處理(下一節)。
使用模塊級記錄器是一個很好的約定,這樣模塊名稱而不是root將顯示為name日志條目的屬性。
如果存在需要多個設置的情況,可以定義并使用自定義記錄器來代替root記錄器。
模塊級記錄器和自定義記錄器可以這樣實現,
import logging
logging.basicConfig(...)
logger=logging.getLogger(__name__)
logger=logging.getLogger("custom_logger")
logger.info("Log with info-level severity")
請注意,日志調用現在使用logger.info()而不是logging.info()!
處理程序將日志記錄發送到適當的目的地(類似于日志文件)
處理程序指定如何保存日志。在前面的部分中,我們僅將日志保存到日志文件中,但還有更多處理日志的方法。這可以通過使用已經為您實現的相關處理程序類來完成。常見的處理程序類包括,
模塊化:處理程序以模塊化方式實現,因此可以定義處理程序名稱并在不同的記錄器(root或自定義記錄器)之間重用。
多對一:一個記錄器可以由多個處理程序組成,例如,如果我們希望日志同時出現在控制臺上,StreamHandler使用FileHandler.
過濾器根據嚴重性確定輸出哪些日志記錄(類似于日志級別)
過濾器設置日志嚴重級別并記錄指定嚴重級別及以上的所有內容。過濾器與處理程序一起在字段下定義level。
如果需要更多自定義,例如僅過濾一個特定的嚴重級別,則必須編寫一個 Python 類,并且此過濾器將與處理程序一起定義,但現在位于 field 下filters。
模塊化:過濾器以模塊化的方式實現,這樣過濾器名稱可以在不同的處理程序中定義和重用。
多對一:一個處理程序可以由多個過濾器組成。
Formatters 指定日志記錄的格式(類似于日志格式)
格式化程序設置日志條目的格式。格式化程序與處理程序一起定義,在字段下formatter。
模塊化:格式化程序以模塊化方式實現,這樣格式化程序名稱可以在不同的處理程序中定義和重用。
一對一:每個處理程序只能有一種日志格式。
每個處理程序都有一個唯一的文件過濾器格式設置
由于過濾器和格式化程序是與處理程序一起定義的,因此每個處理程序都有一個唯一的文件過濾器格式設置。如果需要另一個文件(StreamHandler或FileHandler)、過濾器(DEBUG或WARN級別)或格式,則應定義單獨的處理程序。
我們現在將使用配置文件實現日志記錄——它比使用basicConfig. 可以在.conf文件或字典中定義配置.py或.yml文件
字典配置可以這樣定義,
import logging.config
class LevelOnlyFilter:
def __init__(self, level):
self.level=level
def filter(self, record):
return record.levelno==self.level
LOGGING_CONFIG={
"version": 1,
"loggers": {
"": { # root logger
"level": "DEBUG",
"propagate": False,
"handlers": ["stream_handler", "file_handler"],
},
"custom_logger": {
"level": "DEBUG",
"propagate": False,
"handlers": ["stream_handler"],
},
},
"handlers": {
"stream_handler": {
"class": "logging.StreamHandler",
"stream": "ext://sys.stdout",
"level": "DEBUG",
"filters": ["only_warning"],
"formatter": "default_formatter",
},
"file_handler": {
"class": "logging.FileHandler",
"filename": "output.log",
"mode": "w",
"level": "DEBUG",
"formatter": "default_formatter",
},
},
"filters": {
"only_warning": {
"()": LevelOnlyFilter,
"level": logging.WARN,
},
},
"formatters": {
"default_formatter": {
"format": "%(asctime)s-%(levelname)s-%(name)s::%(module)s|%(lineno)s:: %(message)s",
},
},
}
logging.config.dictConfig(LOGGING_CONFIG)
logger=logging.getLogger(__name__)
我們可以觀察到字典配置被分成幾個部分
具有.conf文件的等效實現可以是這樣的,
[loggers]
keys=root,customLogger
[handlers]
keys=streamHandler,fileHandler
[formatters]
keys=defaultFormatter
[logger_root]
level=DEBUG
propagate=0
handlers=streamHandler,fileHandler
[logger_customLogger]
level=DEBUG
propagate=0
handlers=streamHandler
qualname=customLogger
[handler_streamHandler]
class=StreamHandler
args=(sys.stdout,)
level=DEBUG
formatter=defaultFormatter
[handler_fileHandler]
class=FileHandler
args=("output.log", "w")
level=DEBUG
formatter=defaultFormatter
[formatter_defaultFormatter]
format=%(asctime)s:%(levelname)s:%(name)s:%(module)s:%(funcName)s:%(lineno)s:%(message)s
要使用中定義的配置初始化記錄器logging.conf,
import logging.config
logging.config .fileConfig("logging.conf")
logger=logging.getLogger(__name__)
比較dictConfig和fileConfig,字典實現是首選,因為它更新并且能夠支持更多功能,例如使用自定義過濾器。
比較logging.config和logging.basicConfig,使用配置文件可以減少重復代碼的數量,并將配置抽象到單獨的字典或配置文件中,這是跨多個文件執行日志記錄的首選方式。
在本節中,我將討論使用配置文件實現日志記錄的一些常見問題和注意事項。
有幾種方式可以發生日志配置覆蓋。
№1:同時使用logging.basicConfig和logging.config
如果不同的文件使用不同的方法來實例化記錄器,并且在這些文件之間進行了一些導入,使得日志配置中存在沖突,則logging.config實現優先logging.basicConfig并被覆蓋。
№2:在記錄器級別與處理程序級別定義的日志級別
如果您在字典和logging.conf文件中注意到,日志級別被定義了兩次,一次在記錄器定義中,另一次在處理程序定義中。較高的嚴重級別優先,其次是記錄器級別優先于處理程序級別。
為了使這項工作對您有利,我們可以將記錄器級別設置為最低嚴重性,DEBUG并使用處理程序級別來控制級別。如果一個級別應該跨多個處理程序標準化,請隨意在記錄器定義中定義級別。
在繼承期間,日志將被傳遞給記錄器及其高級(祖先)記錄器,從而導致日志條目重復。默認情況下,所有自定義記錄器都從root記錄器繼承。
為了防止繼承導致重復日志,我們可以在配置中定義記錄器時指定propagate字段。False
希望您對日志記錄的組件以及如何使用配置文件以可重用和可擴展的方式實現它有更多的了解!與普通的打印語句相比,日志記錄有助于開發人員更好地控制和理解代碼庫,并且在調試大型項目中的代碼時特別有用。
感謝您的閱讀!如果您喜歡這篇文章,請隨時分享
文轉載自微信公眾號“前端先鋒”(jingchengyideng)。
每個前端都會用 JavaScript 控制臺打開日志或調試。但是 console 對象比 console.log() 有更多的東西。
ES6 的計算屬性名稱特別有用,因為它們可以通過在變量周圍添加一對大括號來幫你識別的變量。
const x=1, y=2, z=3;
console.log({x, y, z}); // {x: 1, y: 2, z: 3}
console.trac() 與 console.log() 完全相同,但它也會輸出整個棧跟蹤,能讓你確切地知道到底發生了什么。
const outer=()=> {
const inner=()=> console.trace('Hello');
inner();
};
outer();
/*
Hello
inner @ VM207:3
outer @ VM207:5
(anonymous) @ VM228:1
*/
console.group() 可以把日志分組為可折疊的結構,當有多個日志時特別有用。
console.group('Outer'); // 創建一個名為 'Outer' 的組
console.log('Hello'); // 在 'Outer' 組中輸出日志
console.groupCollapsed('Inner'); // 創建一個名為 'Inner' 的組,折疊狀態
console.log('Hellooooo'); // 在 'Inner' 組中輸出日志
console.groupEnd(); // 結束當前組, 'Inner'
console.groupEnd(); // 結束當前組, 'Outer'
console.log('Hi'); // 在組外輸出日志
除了 console.log() 之外,還有其他一些日志記錄級別,例如 [console.debug()](https://developer.mozilla.org/en-US/docs/Web/API/Console /debug)、 console.info()、 console.warn()和 console.error()。
console.debug('Debug message');
console.info('Useful information');
console.warn('This is a warning');
console.error('Something went wrong!');
console.assert() 提供了一種簡便的方法,僅在斷言失敗時將某些內容記錄為錯誤(例如當第一個參數為 false 時),否則完全跳過日志。
const value=10;
console.assert(value===10, 'Value is not 10!'); // 不輸出日志
console.assert(value===20, 'Value is not 20!'); // 輸出日志:"Value is not 20!"
可以用 console.count() 來統計一段代碼執行了多少次。
Array.from({ length: 4 }).forEach(
()=> console.count('items') // 名為 items 的計數器
);
/*
items: 1
items: 2
items: 3
items: 4
*/
console.countReset('items'); // 重置計數器
console.time() 提供了一種快速檢查代碼性能的方法,但是由于精度較低,不可用于真正的基準測試。
console.time('slow comp');
console.timeLog('slow comp');
console.timeEnd('slow comp');
最后一個,還可以在 console.log() 中用 %c 字符串替換表達式將 CSS 應用于日志的各個部分。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。