開發人員都知道,在系統運行中要記錄各種日志,自己寫一個日志功能,無論是在效率還是功能擴展上來說都不是很好,目前大多用的是第三方的日志系統,其中一個非常有名,用的最多的就是log4net。下面是關於這個組件的介紹
”Log4net是基於.net開發的一款非常著名的記錄日志開源組件。最早是2001年7月由NeoWorks Limited啟動的項目,基本的框架源, 於另外的一個非常著名的姐妹組件-log4j。現由Apache組織開發與維護。此日志架構是可靈活擴展,且通過配置文件來設置日志的屬性及輸出,不同修 改代碼即可實現程序的靈活跟蹤。可以將日志分不同的等級,通過不同的過濾條件,以不同的樣式,將日志輸出到不同的媒介。可以從http://logging.apache.org/log4net/l網站下載最新版本的Log4net源碼。“
目前的log4net的最新版本是1.2.13.0
下面介紹下如何在通用權限管理系統的項目中使用這個組件實現將日志輸出到文件和Oracle數據庫中,重點介紹下日志輸出到數據庫中。
首先在官網下載最新源碼,目前的源碼可用VS2010打開。
源碼中已經實現了可以日志輸出到MSSQL的功能,但我的項目目前使用的都是Oracle數據庫,源碼中是沒有實現的,需要自己實現一下:
OracleAppender.cs源碼
public class OracleAppender : BufferingAppenderSkeleton { // Fields private static readonly Type declaringType = typeof(AdoNetAppender); private string m_commandText; private CommandType m_commandType = CommandType.Text; private string m_connectionString; private string m_connectionType; private OracleCommand m_dbCommand; private OracleConnection m_dbConnection; protected ArrayList m_parameters = new ArrayList(); private bool m_reconnectOnError = false; private SecurityContext m_securityContext; protected bool m_usePreparedCommand; private bool m_useTransactions = true; // Methods public override void ActivateOptions() { base.ActivateOptions(); this.m_usePreparedCommand = (this.m_commandText != null) && (this.m_commandText.Length > 0); if (this.m_securityContext == null) { this.m_securityContext = SecurityContextProvider.DefaultProvider.CreateSecurityContext(this); } this.InitializeDatabaseConnection(); this.InitializeDatabaseCommand(); } public void AddParameter(OracleAppenderParameter parameter) { this.m_parameters.Add(parameter); } protected virtual string GetLogStatement(LoggingEvent logEvent) { if (this.Layout == null) { this.ErrorHandler.Error("ADOAppender: No Layout specified."); return ""; } StringWriter writer = new StringWriter(CultureInfo.InvariantCulture); this.Layout.Format(writer, logEvent); return writer.ToString(); } private void InitializeDatabaseCommand() { if ((this.m_dbConnection != null) && this.m_usePreparedCommand) { try { this.m_dbCommand = this.m_dbConnection.CreateCommand(); this.m_dbCommand.CommandText = this.m_commandText; this.m_dbCommand.CommandType = this.m_commandType; } catch (Exception exception) { this.ErrorHandler.Error("Could not create database command [" + this.m_commandText + "]", exception); if (this.m_dbCommand != null) { try { this.m_dbCommand.Dispose(); } catch { } this.m_dbCommand = null; } } if (this.m_dbCommand != null) { try { foreach (OracleAppenderParameter parameter in this.m_parameters) { try { parameter.Prepare(this.m_dbCommand); } catch (Exception exception2) { this.ErrorHandler.Error("Could not add database command parameter [" + parameter.ParameterName + "]", exception2); throw; } } } catch { try { this.m_dbCommand.Dispose(); } catch { } this.m_dbCommand = null; } } if (this.m_dbCommand != null) { try { this.m_dbCommand.Prepare(); } catch (Exception exception3) { this.ErrorHandler.Error("Could not prepare database command [" + this.m_commandText + "]", exception3); try { this.m_dbCommand.Dispose(); } catch { } this.m_dbCommand = null; } } } } private void InitializeDatabaseConnection() { try { this.m_dbConnection = new OracleConnection(); this.m_dbConnection.ConnectionString = this.m_connectionString; using (this.SecurityContext.Impersonate(this)) { this.m_dbConnection.Open(); } } catch (Exception exception) { this.ErrorHandler.Error("Could not open database connection [" + this.m_connectionString + "]", exception); this.m_dbConnection = null; } } protected override void OnClose() { base.OnClose(); if (this.m_dbCommand != null) { this.m_dbCommand.Dispose(); this.m_dbCommand = null; } if (this.m_dbConnection != null) { this.m_dbConnection.Close(); this.m_dbConnection = null; } } protected virtual Type ResolveConnectionType() { Type type; try { type = SystemInfo.GetTypeFromString(this.m_connectionType, true, false); } catch (Exception exception) { this.ErrorHandler.Error("Failed to load connection type [" + this.m_connectionType + "]", exception); throw; } return type; } protected override void SendBuffer(LoggingEvent[] events) { if (this.m_reconnectOnError && ((this.m_dbConnection == null) || (this.m_dbConnection.State != ConnectionState.Open))) { LogLog.Debug(declaringType, "OracleAppender: Attempting to reconnect to database. Current Connection State: " + ((this.m_dbConnection == null) ? "<null>" : this.m_dbConnection.State.ToString())); this.InitializeDatabaseConnection(); this.InitializeDatabaseCommand(); } if ((this.m_dbConnection != null) && (this.m_dbConnection.State == ConnectionState.Open)) { if (this.m_useTransactions) { OracleTransaction dbTran = null; try { dbTran = this.m_dbConnection.BeginTransaction(); this.SendBuffer(dbTran, events); dbTran.Commit(); } catch (Exception exception) { if (dbTran != null) { try { dbTran.Rollback(); } catch (Exception) { } } this.ErrorHandler.Error("Exception while writing to database", exception); } } else { this.SendBuffer(null, events); } } } protected virtual void SendBuffer(OracleTransaction dbTran, LoggingEvent[] events) { if (!this.m_usePreparedCommand) { throw new NotImplementedException(); } if (this.m_dbCommand != null) { ArrayList list = null; foreach (OracleAppenderParameter parameter in this.m_parameters) { list = new ArrayList(); OracleParameter parameter2 = this.m_dbCommand.Parameters[parameter.ParameterName]; foreach (LoggingEvent event2 in events) { object obj2 = parameter.Layout.Format(event2); if (obj2.ToString() == "(null)") { obj2 = DBNull.Value; } list.Add(obj2); } parameter2.Value = list.ToArray(); } this.m_dbCommand.ArrayBindCount = events.Length; this.m_dbCommand.ExecuteNonQuery(); } } // Properties public string CommandText { get { return this.m_commandText; } set { this.m_commandText = value; } } public CommandType CommandType { get { return this.m_commandType; } set { this.m_commandType = value; } } protected OracleConnection Connection { get { return this.m_dbConnection; } set { this.m_dbConnection = value; } } public string ConnectionString { get { return this.m_connectionString; } set { this.m_connectionString = value; } } public bool ReconnectOnError { get { return this.m_reconnectOnError; } set { this.m_reconnectOnError = value; } } public SecurityContext SecurityContext { get { return this.m_securityContext; } set { this.m_securityContext = value; } } public bool UseTransactions { get { return this.m_useTransactions; } set { this.m_useTransactions = value; } } } View CodeOracleAppenderParameter.cs源碼
public class OracleAppenderParameter { // Fields private DbType m_dbType; private bool m_inferType = true; private IRawLayout m_layout; private string m_parameterName; private byte m_precision = 0; private byte m_scale = 0; private int m_size = 0; // Methods public virtual void FormatValue(OracleCommand command, LoggingEvent loggingEvent) { OracleParameter parameter = command.Parameters[this.m_parameterName]; parameter.Value = this.Layout.Format(loggingEvent); } public virtual void Prepare(OracleCommand command) { OracleParameter param = command.CreateParameter(); param.ParameterName = this.m_parameterName; if (!this.m_inferType) { param.DbType = this.m_dbType; } if (this.m_precision != 0) { param.Precision = this.m_precision; } if (this.m_scale != 0) { param.Scale = this.m_scale; } if (this.m_size != 0) { param.Size = this.m_size; } command.Parameters.Add(param); } // Properties public DbType DbType { get { return this.m_dbType; } set { this.m_dbType = value; this.m_inferType = false; } } public IRawLayout Layout { get { return this.m_layout; } set { this.m_layout = value; } } public string ParameterName { get { return this.m_parameterName; } set { this.m_parameterName = value; } } public byte Precision { get { return this.m_precision; } set { this.m_precision = value; } } public byte Scale { get { return this.m_scale; } set { this.m_scale = value; } } public int Size { get { return this.m_size; } set { this.m_size = value; } } } View Code
將源碼重新編譯,生成log4net.dll文件,下面的項目將引用這個類庫文件。
為了在程序中更方便的將日志輸出到文件、Email、數據庫中,封裝了一下日志輸出功能,DotNet.Log是封裝的類庫
//------------------------------------------------------------------------------------- // All Rights Reserved , Copyright (C) 2014 , ZTO , Ltd . //------------------------------------------------------------------------------------- using System; using System.Configuration; using System.IO; namespace DotNet.Log { using DotNet.Utilities; using log4net; using log4net.Appender; /// <summary> /// ZTOLog4NetHelper /// /// 修改紀錄 /// /// 系統運行日志可寫入到文本文件或數據庫(ORACLE) /// /// 2014-10-17 版本:1.0 SongBiao 創建文件。 /// /// <author> /// <name>SongBiao</name> /// <date>2014-10-17</date> /// </author> /// </summary> public class LogHelper { /// <summary> /// 日志對象 沒有在配置文件設置(沒有類定義) 則通過反射 /// 方法使用root的配置 /// </summary> private static ILog log; private static LogHelper logHelper = null; /// <summary> /// 初始化 /// </summary> /// <returns></returns> public static ILog GetInstance() { logHelper = new LogHelper(null); return log; } /// <summary> /// 初始化 /// </summary> /// <param name="configPath"></param> /// <returns></returns> public static ILog GetInstance(string configPath) { logHelper = new LogHelper(configPath); return log; } /// <summary> /// 構造函數 /// </summary> /// <param name="configPath"></param> private LogHelper(string configPath) { if (!string.IsNullOrEmpty(configPath)) { log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); //log4net.Config.XmlConfigurator.Configure(new System.IO.FileInfo(configPath)); //初始化Log4net log4net.Config.XmlConfigurator.ConfigureAndWatch(new FileInfo(System.Web.HttpContext.Current.Server.MapPath(configPath))); } else { log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); } } #region 日志寫入到文件 /// <summary> /// 調試日志 /// </summary> private static readonly ILog logDebugFile = DotNetLogManager.GetLogger("LogDebug"); /// <summary> /// 寫入調試日志 /// </summary> /// <param name="info"></param> public static void WriteDebugLog(string info) { if (logDebugFile.IsInfoEnabled) { logDebugFile.Info(info); } } /// <summary> /// 信息日志 /// </summary> private static readonly ILog logInfoFile = DotNetLogManager.GetLogger("LogInfo"); /// <summary> /// 寫入信息日志 /// </summary> /// <param name="info"></param> public static void WriteInfoLog(string info) { if (logInfoFile.IsInfoEnabled) { logInfoFile.Info(info); } } /// <summary> /// 警告日志 /// </summary> private static readonly ILog logErrorFile = DotNetLogManager.GetLogger("LogError"); /// <summary> /// 寫入錯誤異常日志 /// </summary> /// <param name="info"></param> /// <param name="se"></param> public static void WriteErrorLog(string info, Exception se) { if (logErrorFile.IsErrorEnabled) { logErrorFile.Error(info, se); } } /// <summary> /// 警告日志 /// </summary> private static readonly ILog logWarnFile = DotNetLogManager.GetLogger("LogWarn"); /// <summary> /// 寫入警告日志 /// </summary> /// <param name="info"></param> /// <param name="se"></param> public static void WriteWarnLog(string info, Exception se) { if (logWarnFile.IsErrorEnabled) { logWarnFile.Error(info, se); } } /// <summary> /// 毀滅級別日志 /// </summary> private static readonly ILog logFatalFile = DotNetLogManager.GetLogger("LogFatal"); /// <summary> /// 寫入毀滅級別日志 /// </summary> /// <param name="info"></param> /// <param name="se"></param> public static void WriteFatalLog(string info, Exception se) { if (logFatalFile.IsFatalEnabled) { logFatalFile.Error(info, se); } } /// <summary> /// 非安全行為日志記錄 /// </summary> private static readonly log4net.ILog logSafeFile = DotNetLogManager.GetLogger("LogSafe"); /// <summary> /// 非安全行為日志記錄 /// </summary> /// <param name="info"></param> public static void WriteSafeLog(string info) { if (logSafeFile.IsInfoEnabled) { logSafeFile.Info(info); } } /// <summary> /// 緩存記錄變動的日志對象 當前配置是輸出到文件中 /// </summary> private static readonly ILog logCacheFile = DotNetLogManager.GetLogger("logcache"); /// <summary> /// 寫入緩存變動日志 /// </summary> /// <param name="info"></param> public static void WriteCacheLog(string info) { if (logCacheFile.IsInfoEnabled) { logCacheFile.Info(info); } } #endregion #region 發送到郵箱 /// <summary> /// 發送到郵箱 /// </summary> private static readonly ILog logEmailWarn = DotNetLogManager.GetLogger("LogEmail"); /// <summary> /// 郵件通知 一般是警告類或異常類 內網可能發不了 /// </summary> /// <param name="info"></param> /// <param name="se"></param> public static void WriteEmailLog(string info, Exception se) { if (logEmailWarn.IsInfoEnabled) { logEmailWarn.Error(info, se); } } #endregion #region 日志寫入到Oracle數據庫表 /// <summary> /// Oracle數據庫記錄 /// </summary> public static IZTOLog logOracle = DotNetLogManager.GetLogger("LogOracle"); /// <summary> /// 5. 調試級別 /// </summary> /// <param name="userInfo">用戶信息</param> /// <param name="message">日志信息</param> /// <param name="type">出錯類的類名,可自定義</param> public static void OracleDebug(BaseUserInfo userInfo, string title, object message, string functionName, Type type) { logOracle.Debug(userInfo, type.ToString(), functionName, title, message); } /// <summary> /// 4. 消息級別 /// </summary> /// <param name="userInfo">用戶信息</param> /// <param name="message">日志信息</param> /// <param name="type">出錯類的類名,可自定義</param> public static void OracleInfo(BaseUserInfo userInfo, string title, object message, string functionName, Type type) { logOracle.Info(userInfo, type.ToString(), functionName, title, message); } /// <summary> /// 3. 警告級別 /// </summary> /// <param name="userInfo">用戶信息</param> /// <param name="message">日志信息</param> /// <param name="type">出錯類的類名,可自定義</param> public static void OracleWarn(BaseUserInfo userInfo, string title, object message, string functionName, Type type) { logOracle.Warn(userInfo, type.ToString(), functionName, title, message, null); } /// <summary> /// 3. 警告級別 帶異常 /// </summary> /// <param name="userInfo">用戶信息</param> /// <param name="message">日志信息</param> /// <param name="type">出錯類的類名,可自定義</param> public static void OracleWarn(BaseUserInfo userInfo, string title, object message, string functionName, Type type, System.Exception ex) { logOracle.Warn(userInfo, type.ToString(), functionName, title, message, ex); } /// <summary> /// 2. 錯誤級別 不帶異常 /// </summary> /// <param name="userInfo"></param> /// <param name="message"></param> /// <param name="functionName"></param> /// <param name="type"></param> public static void OracleError(BaseUserInfo userInfo, string title, object message, string functionName, Type type) { logOracle.Error(userInfo, type.ToString(), functionName, title, message, null); } /// <summary> /// 2. 錯誤級別 待異常 /// </summary> /// <param name="userInfo">用戶信息</param> /// <param name="title">日志標題</param> /// <param name="message">日志信息</param> /// <param name="type">出錯類的類名,可自定義</param> /// <param name="Exception">異常信息</param> public static void OracleError(BaseUserInfo userInfo, string title, object message, string functionName, Type type, System.Exception ex) { logOracle.Error(userInfo, type.ToString(), functionName, title, message, ex); } /// <summary> /// 1. 毀滅級別 不帶異常 /// </summary> /// <param name="userInfo"></param> /// <param name="title">日志標題</param> /// <param name="message"></param> /// <param name="functionName"></param> /// <param name="type"></param> public static void OracleFatal(BaseUserInfo userInfo, string title, object message, string functionName, Type type) { logOracle.Fatal(userInfo, type.ToString(), functionName, title, message, null); } /// <summary> /// 1. 毀滅級別 帶異常 /// </summary> /// <param name="userInfo">用戶信息</param> /// <param name="title">日志標題</param> /// <param name="message">日志信息</param> /// <param name="type">出錯類的類名,可自定義</param> public static void OracleFatal(BaseUserInfo userInfo, string title, object message, string functionName, Type type, System.Exception ex) { logOracle.Fatal(userInfo, type.ToString(), functionName, title, message, ex); } #endregion } } /* Oracle數據庫表創建 -- Create table create table SECURITY_LOG4NET ( LOG_ID NUMBER not null, LOG_DATE DATE, LOG_LEVEL VARCHAR2(255), LOG_MESSAGE VARCHAR2(4000), LOG_EXCEPTION VARCHAR2(4000), LOG_LOGGER VARCHAR2(255), LOG_SOURCE VARCHAR2(1000), USERID NUMBER, LOGTYPE VARCHAR2(255), IP VARCHAR2(20), FUNCTIONNAME VARCHAR2(255), REALNAME VARCHAR2(50), SYSTEMCODE VARCHAR2(200), LOG_TITLE VARCHAR2(60) ) tablespace USERS pctfree 10 initrans 1 maxtrans 255 storage ( initial 64K next 8K minextents 1 maxextents unlimited ); -- Add comments to the columns comment on column SECURITY_LOG4NET.LOG_ID is 'ID主鍵'; comment on column SECURITY_LOG4NET.LOG_DATE is '記錄時間'; comment on column SECURITY_LOG4NET.LOG_LEVEL is '日志級別:調試級別:Debug,消息級別:Info,警告級別:Warn,錯誤級別:Error,毀滅級別:Fatal'; comment on column SECURITY_LOG4NET.LOG_MESSAGE is '操作描述'; comment on column SECURITY_LOG4NET.LOG_EXCEPTION is '異常消息'; comment on column SECURITY_LOG4NET.LOG_LOGGER is '日志記錄器'; comment on column SECURITY_LOG4NET.LOG_SOURCE is '日志源'; comment on column SECURITY_LOG4NET.USERID is '用戶主鍵ID'; comment on column SECURITY_LOG4NET.LOGTYPE is '操作類'; comment on column SECURITY_LOG4NET.IP is '操作IP'; comment on column SECURITY_LOG4NET.FUNCTIONNAME is '方法名稱'; comment on column SECURITY_LOG4NET.REALNAME is '用戶姓名'; comment on column SECURITY_LOG4NET.SYSTEMCODE is '子系統編號'; comment on column SECURITY_LOG4NET.LOG_TITLE is '日志標題'; -- Create/Recreate primary, unique and foreign key constraints alter table SECURITY_LOG4NET add constraint SECURITY_LOG4NET_PK primary key (LOG_ID) using index tablespace USERS pctfree 10 initrans 2 maxtrans 255 storage ( initial 64K next 1M minextents 1 maxextents unlimited ); */ View Code
項目中將日志記錄到各個介質就引用這個LogHelper了。
在項目中再引用DotNet.Log,我的是一個web項目,需要設置一個配置文件。
<?xml version="1.0" encoding="utf-8"?> <log4net> <root> <!--<level value="INFO" />--> <!--啟用按日期分割--> <!--<appender-ref ref="LogFileAppenderByDate" />--> <!--啟用按容量分割--> <!--<appender-ref ref="LogFileAppenderBySize" />--> <!--啟用保存到數據庫--> <!--<appender-ref ref="AdoNetAppender" />--> </root> <!--DEBUG 調試信息記錄器--> <logger name="LogDebug" additivity="false"> <level value="DEBUG" /> <appender-ref ref="logger.DebugAppender" /> </logger> <appender name="logger.DebugAppender" type="log4net.Appender.RollingFileAppender"> <param name="File" value="LOG//DEBUG//" /> <!--是否續寫 True/false,默認為true。當文件存在時,是否在原文件上追加內容。--> <param name="AppendToFile" value="true" /> <!--單個文件大小 當RollingStyle為Composite或Size,這裡設置最大文件大小(可以KB,MB,GB為單位,默認為字節)--> <param name="MaxFileSize" value="10240" /> <!--備份日志數目 默認為0 可設最多保存多少個文件--> <param name="MaxSizeRollBackups" value="0" /> <!--寫入的文件名 為true時,RollingStyler的date值將無效。且為true時,需要在file裡指定文件名,所有日志都會記錄在這個文件裡。--> <param name="StaticLogFileName" value="false" /> <!--當RollingStyle為Composite或Date,這裡設置文件名格式--> <param name="DatePattern" value="yyyyMMdd".txt"" /> <!--創建新文件的方式,可選為Size(按文件大小),Date(按日期), Once(每啟動一次創建一個文件),Composite(按日期及文件大小),默認為Composite--> <param name="RollingStyle" value="Date" /> <!--log4net的日志輸出格式--> <layout type="log4net.Layout.PatternLayout"> <param name="Header" value="◇◇◇◇◇開始◇◇◇◇◇◇" /> <param name="Footer" value="◆◆◆◆◆結束◆◆◆◆◆◆" /> <param name="ConversionPattern" value="%n時間:%d %n級別:%level %n類名:%c%n文件:%F 第%L行%n日志內容:%m%n-----------------------------------------%n" /> </layout> </appender> <!--INFO 普通信息記錄器--> <logger name="LogInfo" additivity="false"> <level value="INFO" /> <appender-ref ref="logger.InfoAppender" /> </logger> <appender name="logger.InfoAppender" type="log4net.Appender.RollingFileAppender"> <param name="File" value="LOG//INFO//" /> <!--是否續寫 True/false,默認為true。當文件存在時,是否在原文件上追加內容。--> <param name="AppendToFile" value="true" /> <!--單個文件大小 當RollingStyle為Composite或Size,這裡設置最大文件大小(可以KB,MB,GB為單位,默認為字節)--> <param name="MaxFileSize" value="10240" /> <!--備份日志數目 默認為0 可設最多保存多少個文件--> <param name="MaxSizeRollBackups" value="0" /> <!--寫入的文件名 為true時,RollingStyler的date值將無效。且為true時,需要在file裡指定文件名,所有日志都會記錄在這個文件裡。--> <param name="StaticLogFileName" value="false" /> <!--當RollingStyle為Composite或Date,這裡設置文件名格式--> <param name="DatePattern" value="yyyyMMdd".txt"" /> <!--創建新文件的方式,可選為Size(按文件大小),Date(按日期), Once(每啟動一次創建一個文件),Composite(按日期及文件大小),默認為Composite--> <param name="RollingStyle" value="Date" /> <!--log4net的日志輸出格式--> <layout type="log4net.Layout.PatternLayout"> <param name="Header" value="◇◇◇◇◇開始◇◇◇◇◇◇" /> <param name="Footer" value="◆◆◆◆◆結束◆◆◆◆◆◆" /> <param name="ConversionPattern" value="%n時間:%d %n級別:%level %n類名:%c%n文件:%F 第%L行%n日志內容:%m%n-----------------------------------------%n" /> </layout> </appender> <!--ERROR 錯誤記錄器--> <logger name="LogError" additivity="false"> <level value="ERROR" /> <appender-ref ref="logger.ErrorAppender" additivity="false"/> </logger> <appender name="logger.ErrorAppender" type="log4net.Appender.RollingFileAppender"> <!--是否續寫--> <param name="AppendToFile" value="true" /> <!--備份日志數目 默認為0 可設最多保存多少個文件--> <param name="MaxSizeRollBackups" value="0" /> <!--單個文件大小--> <param name="MaxFileSize" value="10240" /> <!--寫入的文件名 為true時,RollingStyler的date值將無效。且為true時,需要在file裡指定文件名,所有日志都會記錄在這個文件裡。--> <param name="StaticLogFileName" value="false" /> <param name="File" value="Log//ERROR//" /> <param name="DatePattern" value="yyyyMMdd".txt""/> <param name="RollingStyle" value="Date" /> <!--log4net的日志輸出格式--> <layout type="log4net.Layout.PatternLayout"> <param name="Header" value="◇◇◇◇◇開始◇◇◇◇◇◇" /> <param name="Footer" value="◆◆◆◆◆結束◆◆◆◆◆◆" /> <!--<param name="ConversionPattern" value="%n%d [%t] %-5p %c [%x] - %m%n%n%n" />--> <param name="ConversionPattern" value="%n時間:%d %n級別:%level %n類名:%c%n文件:%F 第%L行%n日志內容:%m%n-----------------------------------------%n" /> </layout> </appender> <!--WARN 警告日志記錄器--> <logger name="LogWarn" additivity="false"> <level value="WARN" /> <appender-ref ref="logger.WarnAppender" additivity="false"/> </logger> <appender name="logger.WarnAppender" type="log4net.Appender.RollingFileAppender"> <!--是否續寫--> <param name="AppendToFile" value="true" /> <!--備份日志數目 默認為0 可設最多保存多少個文件--> <param name="MaxSizeRollBackups" value="0" /> <!--單個文件大小--> <param name="MaxFileSize" value="10240" /> <!--寫入的文件名 為true時,RollingStyler的date值將無效。且為true時,需要在file裡指定文件名,所有日志都會記錄在這個文件裡。--> <param name="StaticLogFileName" value="false" /> <param name="File" value="LOG//WARN//" /> <param name="DatePattern" value="yyyyMMdd".txt""/> <param name="RollingStyle" value="Date" /> <!--log4net的日志輸出格式--> <layout type="log4net.Layout.PatternLayout"> <param name="Header" value="◇◇◇◇◇開始◇◇◇◇◇◇" /> <param name="Footer" value="◆◆◆◆◆結束◆◆◆◆◆◆" /> <!--<param name="ConversionPattern" value="%n%d [%t] %-5p %c [%x] - %m%n%n%n" />--> <param name="ConversionPattern" value="%n時間:%d %n級別:%level %n類名:%c%n文件:%F 第%L行%n日志內容:%m%n-----------------------------------------%n" /> </layout> </appender> <!--FATAL 毀滅級別日志記錄器--> <logger name="LogFatal" additivity="false"> <level value="FATAL" /> <appender-ref ref="logger.FatalAppender" additivity="false"/> </logger> <appender name="logger.FatalAppender" type="log4net.Appender.RollingFileAppender"> <!--是否續寫--> <param name="AppendToFile" value="true" /> <!--備份日志數目 默認為0 可設最多保存多少個文件--> <param name="MaxSizeRollBackups" value="0" /> <!--單個文件大小--> <param name="MaxFileSize" value="10240" /> <!--寫入的文件名 為true時,RollingStyler的date值將無效。且為true時,需要在file裡指定文件名,所有日志都會記錄在這個文件裡。--> <param name="StaticLogFileName" value="false" /> <param name="File" value="LOG//FATAL//" /> <param name="DatePattern" value="yyyyMMdd".txt""/> <param name="RollingStyle" value="Date" /> <!--log4net的日志輸出格式--> <layout type="log4net.Layout.PatternLayout"> <param name="Header" value="◇◇◇◇◇開始◇◇◇◇◇◇" /> <param name="Footer" value="◆◆◆◆◆結束◆◆◆◆◆◆" /> <!--<param name="ConversionPattern" value="%n%d [%t] %-5p %c [%x] - %m%n%n%n" />--> <param name="ConversionPattern" value="%n時間:%d %n級別:%level %n類名:%c%n文件:%F 第%L行%n日志內容:%m%n-----------------------------------------%n" /> </layout> </appender> <!--CACHE 緩存變更記錄器--> <logger name="LogCache" additivity="false"> <level value="ALL" /> <appender-ref ref="logger.CacheAppender" /> </logger> <appender name="logger.CacheAppender" type="log4net.Appender.RollingFileAppender"> <param name="File" value="LOG//CACHE//" /> <!--是否續寫 True/false,默認為true。當文件存在時,是否在原文件上追加內容。--> <param name="AppendToFile" value="true" /> <!--單個文件大小 當RollingStyle為Composite或Size,這裡設置最大文件大小(可以KB,MB,GB為單位,默認為字節)--> <param name="MaxFileSize" value="10240" /> <!--備份日志數目 默認為0 可設最多保存多少個文件--> <param name="MaxSizeRollBackups" value="0" /> <!--寫入的文件名 為true時,RollingStyler的date值將無效。且為true時,需要在file裡指定文件名,所有日志都會記錄在這個文件裡。--> <param name="StaticLogFileName" value="false" /> <!--當RollingStyle為Composite或Date,這裡設置文件名格式--> <param name="DatePattern" value="yyyyMMdd".txt"" /> <!--創建新文件的方式,可選為Size(按文件大小),Date(按日期), Once(每啟動一次創建一個文件),Composite(按日期及文件大小),默認為Composite--> <param name="RollingStyle" value="Date" /> <!--log4net的日志輸出格式--> <layout type="log4net.Layout.PatternLayout"> <param name="Header" value="◇◇◇◇◇開始◇◇◇◇◇◇" /> <param name="Footer" value="◆◆◆◆◆結束◆◆◆◆◆◆" /> <!--<param name="ConversionPattern" value="%n%d [%t] %-5p %c [%x] - %m%n%n%n" />--> <param name="ConversionPattern" value="%n時間:%d %n級別:%level %n類名:%c%n文件:%F 第%L行%n日志內容:%m%n-----------------------------------------%n" /> </layout> </appender> <!--非安全行為 日志記錄器--> <logger name="LogSafe" additivity="false"> <level value="ALL" /> <appender-ref ref="logger.SafeAppender" /> </logger> <appender name="logger.SafeAppender" type="log4net.Appender.RollingFileAppender"> <param name="File" value="LOG//SAFE//" /> <!--是否續寫 True/false,默認為true。當文件存在時,是否在原文件上追加內容。--> <param name="AppendToFile" value="true" /> <!--單個文件大小 當RollingStyle為Composite或Size,這裡設置最大文件大小(可以KB,MB,GB為單位,默認為字節)--> <param name="MaxFileSize" value="10240" /> <!--備份日志數目 默認為0 可設最多保存多少個文件--> <param name="MaxSizeRollBackups" value="0" /> <!--寫入的文件名 為true時,RollingStyler的date值將無效。且為true時,需要在file裡指定文件名,所有日志都會記錄在這個文件裡。--> <param name="StaticLogFileName" value="false" /> <!--當RollingStyle為Composite或Date,這裡設置文件名格式--> <param name="DatePattern" value="yyyyMMdd".txt"" /> <!--創建新文件的方式,可選為Size(按文件大小),Date(按日期), Once(每啟動一次創建一個文件),Composite(按日期及文件大小),默認為Composite--> <param name="RollingStyle" value="Date" /> <!--log4net的日志輸出格式--> <layout type="log4net.Layout.PatternLayout"> <param name="Header" value="◇◇◇◇◇開始◇◇◇◇◇◇" /> <param name="Footer" value="◆◆◆◆◆結束◆◆◆◆◆◆" /> <!--<param name="ConversionPattern" value="%n%d [%t] %-5p %c [%x] - %m%n%n%n" />--> <param name="ConversionPattern" value="%n時間:%d %n級別:%level %n類名:%c%n文件:%F 第%L行%n日志內容:%m%n-----------------------------------------%n" /> </layout> </appender> <!--EMAIL 發郵件 注意內網可能不能發送郵件 --> <logger name="LogEmail" additivity="false"> <level value="ALL" /> <appender-ref ref="logger.EmaiAppender" /> </logger> <appender name="logger.EmaiAppender" type="log4net.Appender.SMTPAppender"> <authentication value="Basic"/> <subject value="【中通快遞系統】運行異常狀態報告" /> <to value="[email protected]" /> <from value="[email protected]" /> <smtphost value="mail.zto.cn" /> <username value="ztoexception" /> <password value="d1fbf1ff" /> <port value="25"/> <bufferSize value="2048" /> <!--超長部分是否丟棄--> <lossy value="true" /> <!-- 下面的定義, 就是 日志級別 大於 WARN 的, 才發郵件. --> <evaluator type="log4net.Core.LevelEvaluator"> <threshold value="WARN" /> </evaluator> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%property{log4net:HostName} :: %level :: %message %newlineLogger: %logger%newlineThread: %thread%newlineDate: %date%newlineNDC: %property{NDC}%newline%newline" /> </layout> </appender> <!--專門為Oracle寫的日志記錄器 --> <logger name="LogOracle" additivity="false"> <level value="ALL" /> <appender-ref ref="logger.AdoNetAppender_Oracle" /> </logger> <appender name="logger.AdoNetAppender_Oracle" type="DotNet.Log.OracleAdoNetAppender"> <bufferSize value="1" /> <connectionType value="Oracle.DataAccess.Client.OracleConnection, Oracle.DataAccess, Version=4.112.3.0, Culture=neutral, PublicKeyToken=89b483f429c47342" /> <ConnectionStringName value="Log4NetOracleDBConnection" /> <commandText value="INSERT INTO SECURITY_LOG4NET (LOG_ID, LOG_DATE, LOG_LEVEL, LOG_MESSAGE, LOG_EXCEPTION, LOG_LOGGER, LOG_SOURCE,USERID,LOGTYPE,IP,FUNCTIONNAME,REALNAME,LOG_TITLE,SYSTEMCODE) VALUES (SEQ_SECURITY_LOG4NET.nextval, :log_date, :log_level,:log_message, :log_exception, :logger, :source,:USERID,:LOGTYPE,:IP,:FUNCTIONNAME,:REALNAME,:LOG_TITLE,:SYSTEMCODE)" /> <!--注意自定義的字段的大小寫要一致 --> <parameter> <parameterName value=":log_date" /> <dbType value="DateTime" /> <layout type="log4net.Layout.RawTimeStampLayout"> <conversionPattern value="%d{yyyy/MM/dd HH:mm:ss}" /> </layout> </parameter> <parameter> <parameterName value=":log_level" /> <dbType value="String" /> <size value="10" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%level" /> </layout> </parameter> <parameter> <parameterName value=":log_message" /> <dbType value="String" /> <size value="4000" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%m" /> </layout> </parameter> <parameter> <parameterName value=":log_exception" /> <dbType value="String" /> <size value="4000" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%exception" /> </layout> </parameter> <parameter> <parameterName value=":logger" /> <dbType value="String" /> <size value="255" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%logger" /> </layout> </parameter> <parameter> <parameterName value=":source" /> <dbType value="String" /> <size value="1000" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%file:%line" /> </layout> </parameter> <parameter> <parameterName value=":USERID" /> <dbType value="Int32" /> <layout type="DotNet.Log.CustomLayout"> <conversionPattern value="%property{UserID}" /> </layout> </parameter> <parameter> <parameterName value=":LOGTYPE" /> <dbType value="String" /> <layout type="DotNet.Log.CustomLayout"> <conversionPattern value="%property{LOGTYPE}" /> </layout> </parameter> <parameter> <parameterName value=":IP" /> <dbType value="String" /> <layout type="DotNet.Log.CustomLayout"> <conversionPattern value="%property{IP}" /> </layout> </parameter> <parameter> <parameterName value=":FUNCTIONNAME" /> <dbType value="String" /> <layout type="DotNet.Log.CustomLayout"> <conversionPattern value="%property{FUNCTIONNAME}" /> </layout> </parameter> <parameter> <parameterName value=":REALNAME" /> <dbType value="String" /> <layout type="DotNet.Log.CustomLayout"> <conversionPattern value="%property{REALNAME}" /> </layout> </parameter> <parameter> <parameterName value=":LOG_TITLE" /> <dbType value="String" /> <layout type="DotNet.Log.CustomLayout"> <conversionPattern value="%property{LOG_TITLE}" /> </layout> </parameter> <parameter> <parameterName value=":SYSTEMCODE" /> <dbType value="String" /> <layout type="DotNet.Log.CustomLayout"> <conversionPattern value="%property{SYSTEMCODE}" /> </layout> </parameter> </appender> <!--日志信息輸出到頁面的Trace上下文環--> <logger name="LogAspNetTrace" additivity="false"> <level value="ALL" /> <appender-ref ref="logger.AspNetTraceAppender" /> </logger> <!--這段配置可將日志信息輸出到頁面的Trace上下文環境。 如果日志的級別低於WARN,會以 System.Web.TraceContext.Write方法輸出; 如果級別為WARN或WARN以上則會以 System.Web.TraceContext.Warn方法輸出,輸出的不同顏色可以說明這一點--> <appender name ="logger.AspNetTraceAppender" type ="log4net.Appender.AspNetTraceAppender"> <layout type ="log4net.Layout.PatternLayout" > <conversionPattern value ="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline"/> </layout> </appender> <!--日志信息輸出到控制台 高亮顯示Error信息--> <logger name="LogColoredConsole" additivity="false"> <level value="ALL" /> <appender-ref ref="logger.ColoredConsoleAppender" /> </logger> <!--ColoredConsoleAppender將日志信息輸出到控制台。 默認情況下,日志信息被發送到控制台標准輸出流。下面這個示例演示了如何高亮顯示Error信息。--> <appender name ="logger.ColoredConsoleAppender" type ="log4net.Appender.ColoredConsoleAppender"> <mapping > <level value ="ERROR" /> <foreColor value ="White"/> <backColor value ="Red, HighIntensity"/> </mapping> <mapping> <level value ="DEBUG"/> <backColor value ="Green"/> </mapping> <layout type ="log4net.Layout.PatternLayout" > <conversionPattern value ="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline"/> </layout> </appender> <!--日志信息輸出到控制台 高亮顯示Error信息--> <logger name="LogConsole" additivity="false"> <level value="ALL" /> <appender-ref ref="logger.ConsoleAppender" /> </logger> <!--CONSOLEAPPENDER將日志信息輸出到控制台標准輸出流。--> <appender name ="logger.ConsoleAppender" type ="log4net.Appender.ConsoleAppender"> <layout type ="log4net.Layout.PatternLayout" > <!--<param name ="ConversionPattern" value ="%d [%t] %-5p %c [%x] - %m%n"/>--> <conversionPattern value="[時間]:%d%n[級別]:%p%n[內容]:%m%n%n"></conversionPattern> </layout> </appender> <!--日志寫入本地機器的應用程序事件日志中--> <logger name="LogEventlog" additivity="false"> <level value="ALL" /> <appender-ref ref="logger.EventLogAppender" /> </logger> <!--EventLogAppender將日志寫入本地機器的應用程序事件日志中。 默認情況下,該日志的源(Source)是AppDomain.FriendlyName,也可以手動指定其它名稱。--> <appender name ="logger.EventLogAppender" type ="log4net.Appender.EventLogAppender"> <layout type ="log4net.Layout.PatternLayout" > <param name ="ConversionPattern" value ="%d [%t] %-5p %c [%x] - %m%n"/> </layout> </appender> <!--FileAppender將日志信息輸出到指定的日志文件。 File指定了文件名稱,可以使用相對路徑, 此時日志文件的位置取決於項目的類型(如控制台、Windows Forms、ASP.NET等); 也可以使用絕對路徑;甚至可以使用環境變量,如<file value="${TMP}\log-file.txt" />。 AppendToFile指定是追加到還是覆蓋掉已有的日志文件。還可以添加如下屬性<lockingModel type="log4net.Appender.FileAppender+MinimalLock" /> 來使用最小鎖定模型(minimal locking model),以允許多個進程可以寫入同一個文件。--> <appender name ="LogFileAppender" type ="log4net.Appender.FileAppender"> <param name ="File" value ="WebUtilClient.log"/> <param name ="AppendToFile" value ="true"/> <layout type ="log4net.Layout.PatternLayout"> <param name ="ConversionPattern" value ="%d [%t] %-5p %c [%x] - %m%n"/> </layout> </appender> <!--ForwardingAppender可以用來為一個Appender指定一組約束 在這個示例中,為ConsoleAppender添加了約束,Threshold為WARN。這意味著對於一條日志信息, 如果直接使用 ConsoleAppender,那麼不論它是什麼級別,總會進行輸出,而如果使用這個ForwardingAppender, 則只有那些WARN或 WARN以上的日志才會發送到ConsoleAppender。--> <appender name ="ForwardingAppender" type ="log4net.Appender.ForwardingAppender"> <threshold value ="WARN" /> <appender-ref ref ="ConsoleAppender"/> </appender> <!--似乎不應該使用配置文件來配置MemoryAppender,但如果你非要這麼做,看看這個示例(未驗證):--> <appender name ="MemoryAppender" type ="log4net.Appender.MemoryAppender" > <onlyFixPartialEventData value ="true" /> </appender> <!--NetSendAppender向特定用戶的屏幕發送消息(未驗證)--> <appender name ="NetSendAppender" type ="log4net.Appender.NetSendAppender" > <threshold value ="ERROR"/> <server value ="Anders"/> <recipient value ="xym"/> <layout type ="log4net.Layout.PatternLayout"> <conversionPattern value ="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline"/> </layout> </appender> <!--下面這個例子描述了如何配置該Appender以向OutputDebugString API寫入日志(未驗證)。--> <appender name ="OutputDebugStringAppender" type ="log4net.Appender.OutputDebugStringAppender"> <layout type ="log4net.Layout.PatternLayout" > <conversionPattern value ="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline"/> </layout> </appender> <!--remotingAppender向特定的Sink發送日志信息(未驗證):--> <appender name ="RemotingAppender" type ="log4net.Appender.RemotingAppender"> <sink value ="tcp://localhost:8085/LoggingSink"/> <lossy value ="false"/> <bufferSize value ="95"/> <onlyFixPartialEventData value ="true"/> </appender> <!--如何防止自定義的logger和root重復寫入日志 logger是從root繼承而來,默認的用logger寫的日志會被root重復寫一份, 如何防止這種情況呢,網上有人提出可以去掉root裡的 appender-ref,這不是一個好的解決方法, logger只是為了單獨處理一些日志,大部分的日志還是要依靠root來完成。 其實log4net提供了一種很好的解決方式,斷開logger與root的繼承關系, 只要在配置文件裡的logger加上屬性additivity="false" 就可以了。--> <!--Log4Net 日志級別 Log4net 分為如下幾個級別: FATAL 毀滅級別 ERROR 錯誤級別 WARN 警告級別 INFO 消息級別 DEBUG 調試級別 這幾種日志級別高低:FATAL> ERROR> WARN > INFO > DEBUG。 只有日志輸出級別大於或等於配置的級別才能輸出日志信息。 比如我的日志配置級別為INFO,那麼只有log.Info(), log.Warn(), log.Error(), log.Fatal()才能輸出日志信息,Debug方式就不能輸出。 Log4net中還有兩種特殊的配置級別: ALL - 允許所有的日志級別輸出,OFF - 拒絕所有的日志級別輸出。 %m[%message] : 輸出的日志消息 %n : 換行 %d[%datetime] : 輸出當前語句運行的時刻 %r : 輸出程序從運行到執行到當前語句時消耗的毫秒數 %d : 當前語句所在的線程ID %p : 日志的當前優先級別 %c :當前日志對象的名稱 %L : 輸出語句所在的行號 %F :輸出語句所在的文件名 %-數字 :表示該項的最小長度,如果不夠,則用空格填充 --> </log4net> View Code
在項目的web.config中需要配置的項目
/// <summary> /// 在此處進行安全檢測和防范 /// Application_BeginRequest /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void Application_AcquireRequestState(object sender, EventArgs e) { HttpContext context = HttpContext.Current; string putData = string.Empty; if (Request.Cookies != null) { if (GeneralSafe.CookieData(out putData)) { ResponseWarnMessage(context, "Cookie數據有惡意字符!", putData); } } if (Request.UrlReferrer != null) { if (GeneralSafe.Referer(out putData)) { ResponseWarnMessage(context, "Referrer數據有惡意字符!", putData); } } if (Request.RequestType.ToUpper() == "POST") { if (GeneralSafe.PostData(out putData)) { ResponseWarnMessage(context, "Post數據有惡意字符!", putData); } } if (Request.RequestType.ToUpper() == "GET") { if (GeneralSafe.GetData(out putData)) { ResponseWarnMessage(context, "Get數據有惡意字符!", putData); } } } /// <summary> /// 非安全行為 輸出警告信息 /// </summary> /// <param name="errorMessage"></param> /// <param name="putData"></param> private void ResponseWarnMessage(HttpContext context, string errorMessage, string putData) { //記錄一下惡意攻擊行為 string ipAddress = Utilities.GetIPAddress(true); BaseUserInfo userInfo = context.Session[DotNet.Business.Utilities.SessionName] as BaseUserInfo; //非安全行為同時記錄到數據庫和文本文件中 LogHelper.OracleWarn(userInfo, "惡意訪問行為", "來自IP:" + ipAddress + "的訪問存在惡意行為:" + errorMessage + "字符內容:" + putData, " private void ResponseErrorMessage(string errorMessage, string putData)", typeof(AccountController), null); LogHelper.WriteSafeLog("來自IP:" + ipAddress + "的訪問存在惡意行為:" + errorMessage + "字符內容:" + putData); RouteData routeData = new RouteData(); routeData.Values.Add("controller", "Error"); routeData.Values.Add("action", "General"); routeData.Values.Add("title", "非法訪問與請求提醒"); routeData.Values.Add("error", "你提交的" + errorMessage + "字符內容:" + putData); IController errorController = new ErrorController(); errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData)); context.Response.End(); }
以上代碼可檢測COOKIE,Referrer,POST,GET中傳入的非法字符。頁面顯示效果截圖如下
數據庫日志記錄的內容截圖如下:
文本文件中記錄的內容如下截圖
以上就是基於log4net和通用權限管理系統底層做的一個日志記錄功能,最新版本通用權限管理系統將會使用這個日志功能。
如有改進建議,歡迎大家提出。