【开源】OSharp3.0框架解说系列(6.2):操作日志与数据日志
OSharp是什么?
OSharp是個快速開發(fā)框架,但不是一個大而全的包羅萬象的框架,嚴(yán)格的說,OSharp中什么都沒有實現(xiàn)。與其他大而全的框架最大的不同點,就是OSharp只做抽象封裝,不做實現(xiàn)。依賴注入、ORM、對象映射、日志、緩存等等功能,都只定義了一套最基礎(chǔ)最通用的抽象封裝,提供了一套統(tǒng)一的API、約定與規(guī)則,并定義了部分執(zhí)行流程,主要是讓項目在一定的規(guī)范下進行開發(fā)。所有的功能實現(xiàn)端,都是通過現(xiàn)有的成熟的第三方組件來實現(xiàn)的,除了EntityFramework之外,所有的第三方實現(xiàn)都可以輕松的替換成另一種第三方實現(xiàn),OSharp框架正是要起隔離作用,保證這種變更不會對業(yè)務(wù)代碼造成影響,使用統(tǒng)一的API來進行業(yè)務(wù)實現(xiàn),解除與第三方實現(xiàn)的耦合,保持業(yè)務(wù)代碼的規(guī)范與穩(wěn)定。
本文已同步到系列目錄:OSharp快速開發(fā)框架解說系列
前言
在《【開源】OSharp框架解說系列(6.1):日志系統(tǒng)設(shè)計》中,我們已經(jīng)設(shè)計并實現(xiàn)了一個可擴展的日志系統(tǒng),只要定義好輸出端的Adapter,就可以以任意形式輸出日志信息。
在系統(tǒng)開發(fā)中,有些日志記錄需求是常規(guī)需要的,比如操作日志,數(shù)據(jù)變更日志,系統(tǒng)異常日志等,我們希望把這些常規(guī)需求都集成到OSharp框架當(dāng)中。有了內(nèi)置的支持,在做開發(fā)的時候,只需要很簡單的配置,就可以實現(xiàn)相關(guān)需求。
關(guān)于三類日志,這里先簡要描述一下:
- 操作日志:粗略描述系統(tǒng)用戶(如管理員、業(yè)務(wù)人員、會員等)對系統(tǒng)的業(yè)務(wù)操作,只需要說清楚“XXX用戶在XXX時間做了XXX操作”
- 數(shù)據(jù)日志:有時候,為了追溯用戶的業(yè)務(wù)操作對系統(tǒng)產(chǎn)生的影響,需要記錄數(shù)據(jù)變更細(xì)節(jié),這就是數(shù)據(jù)日志
- 系統(tǒng)日志:主要記錄系統(tǒng)在運行過程中產(chǎn)生的與業(yè)務(wù)無關(guān)的常規(guī)或異常的日志信息,這些日志信息通常由系統(tǒng)維護人員或開發(fā)人員查看
日志記錄準(zhǔn)備
在OSharp框架中,操作日志與數(shù)據(jù)日志的記錄流程如下圖所示:
這里用文字簡單描述一下操作日志與數(shù)據(jù)日志記錄的實現(xiàn)思路:
- 定義了一個“功能信息記錄”的實體,用于提取系統(tǒng)中各個功能點的基礎(chǔ)信息(名稱、MVC的Area-Controller-Action、功能訪問類型(匿名訪問-登錄訪問-特定角色訪問)、是否啟用功能日志,是否啟用數(shù)據(jù)日志、功能URL等),并配置功能的行為
- 定義了一個“實體信息記錄”的實體,用于提取系統(tǒng)中各個數(shù)據(jù)實體類型的基礎(chǔ)信息(實體類型全名、實體名稱、是否啟用數(shù)據(jù)日志,實體屬性信息集),并配置實體的行為
- 系統(tǒng)初始化的時候,通過反射加載的程序集,提取并構(gòu)建各個功能點(主要是MVC的Controller-Action)的功能信息記錄,更新到數(shù)據(jù)庫中
- 系統(tǒng)初始化的時候,通過反射加載的程序集,提取并構(gòu)建各個實體類型的實體信息記錄,更新到數(shù)據(jù)庫中
- 利用MVC框架的ActionFilter進行AOP攔截,定義一個專門用于操作日志記錄的 OperateLogFilterAttribute ,重寫 OnActionExecuted 方法進行操作日志的記錄
- 操作日志與數(shù)據(jù)日志記錄的詳細(xì)流程如下:
- 在用戶的業(yè)務(wù)操作執(zhí)行到保存數(shù)據(jù)的時候(EF執(zhí)行SaveChanges時),根據(jù)操作涉及的實體獲取相應(yīng)的實體信息記錄,確定是否創(chuàng)建數(shù)據(jù)日志,不需創(chuàng)建則跳過
- 需要創(chuàng)建時,根據(jù)實體的狀態(tài)(Added-Modified-Deleted),創(chuàng)建各個實體的新增-更新-刪除的數(shù)據(jù)日志信息,并存儲到臨時緩存中
- 執(zhí)行到 OperateLogFilterAttribute 的 OnActionExecuted 方法的時候,根據(jù)ActionExecutedContext 中提供的Area,Controller,Action等信息,查詢出當(dāng)前功能的功能信息記錄,確定是否記錄操作日志,不需記錄則返回
- 需要根據(jù)功能信息記錄,創(chuàng)建操作日志信息,并指定當(dāng)前用戶為日志操作人。
- 根據(jù)功能信息是否啟用數(shù)據(jù)日志的配置,確定是否記錄數(shù)據(jù)日志,需要記錄時,從臨時緩存中提取前面創(chuàng)建的數(shù)據(jù)日志,作為從數(shù)據(jù)配置到操作日志中
- 向系統(tǒng)外部保存操作日志信息,完成操作日志的記錄
功能信息與實體信息
記錄各個功能點的功能信息接口定義如下:
/// <summary>
/// 功能接口,最小功能信息
/// </summary>
public interface IFunction
{
/// <summary>
/// 獲取或設(shè)置 功能名稱
/// </summary>
string Name { get; set; } /// <summary>
/// 獲取或設(shè)置 區(qū)域名稱
/// </summary>
string Area { get; set; } /// <summary>
/// 獲取或設(shè)置 控制器名稱
/// </summary>
string Controller { get; set; } /// <summary>
/// 獲取或設(shè)置 功能名稱
/// </summary>
string Action { get; set; } /// <summary>
/// 獲取或設(shè)置 功能類型
/// </summary>
FunctionType FunctionType { get; set; } /// <summary>
/// 獲取或設(shè)置 是否啟用操作日志
/// </summary>
bool OperateLogEnabled { get; set; } /// <summary>
/// 獲取或設(shè)置 是否啟用數(shù)據(jù)日志
/// </summary>
bool DataLogEnabled { get; set; } /// <summary>
/// 獲取或設(shè)置 是否鎖定
/// </summary>
bool IsLocked { get; set; } /// <summary>
/// 獲取或設(shè)置 功能地址
/// </summary>
string Url { get; set; }
}
記錄各個數(shù)據(jù)實體類型的實體信息接口定義如下:
/// <summary>
/// 實體數(shù)據(jù)接口
/// </summary>
public interface IEntityInfo
{
/// <summary>
/// 獲取 實體數(shù)據(jù)類型名稱
/// </summary>
string ClassName { get; } /// <summary>
/// 獲取 實體數(shù)據(jù)顯示名稱
/// </summary>
string Name { get; } /// <summary>
/// 獲取 是否啟用數(shù)據(jù)日志
/// </summary>
bool DataLogEnabled { get; } /// <summary>
/// 獲取 實體屬性信息字典
/// </summary>
IDictionary<string, string> PropertyNames { get; }
}
OSharp框架中,已經(jīng)派生了 Function 與 EntityInfo 兩個實體類型,作為功能信息與實體信息的封裝。
功能信息與實體信息的初始化實現(xiàn),主要定義在 FunctionHandlerBase<TFunction, TKey> 與 EntityInfoHandlerBase<TEntityInfo, TKey> 兩個基礎(chǔ)中,OSharp中已經(jīng)派生了 public class DefaultFunctionHandler : FunctionHandlerBase<Function, Guid> 與 public class DefaultEntityInfoHandler : EntityInfoHandlerBase<EntityInfo, Guid> 作為系統(tǒng)初始化時,從程序集中提取并更新功能信息與數(shù)據(jù)信息的默認(rèn)實現(xiàn)。
由代碼圖,我們能很直觀的看到實體與處理器之間的關(guān)系:
關(guān)于這兩個處理器的實現(xiàn)流程,不是本文的重點,將在后面講解OSharp初始化實現(xiàn)時再詳述,這里先略過。提取的數(shù)據(jù)展示如下:
提取的功能信息:
提取的實體數(shù)據(jù)信息:
操作日志與數(shù)據(jù)日志實體
操作日志實體定義如下:
/// <summary>
/// 操作日志信息類
/// </summary>
[Description("系統(tǒng)-操作日志信息")]
public class OperateLog : EntityBase<int>, ICreatedTime
{
/// <summary>
/// 初始化一個<see cref="OperateLog"/>類型的新實例
/// </summary>
public OperateLog()
{
DataLogs = new List<DataLog>();
} /// <summary>
/// 獲取或設(shè)置 執(zhí)行的功能名稱
/// </summary>
[StringLength()]
public string FunctionName { get; set; } /// <summary>
/// 獲取或設(shè)置 操作人信息
/// </summary>
public Operator Operator { get; set; } /// <summary>
/// 獲取設(shè)置 信息創(chuàng)建時間
/// </summary>
public DateTime CreatedTime { get; set; } /// <summary>
/// 獲取或設(shè)置 數(shù)據(jù)日志集合
/// </summary>
public virtual ICollection<DataLog> DataLogs { get; set; }
}
數(shù)據(jù)日志實體定義如下:
/// <summary>
/// 數(shù)據(jù)日志信息類
/// </summary>
[Description("系統(tǒng)-數(shù)據(jù)日志信息")]
public class DataLog : EntityBase<int>
{
/// <summary>
/// 初始化一個<see cref="DataLog"/>類型的新實例
/// </summary>
public DataLog()
: this(null, null, OperatingType.Query)
{ } /// <summary>
/// 初始化一個<see cref="DataLog"/>類型的新實例
/// </summary>
public DataLog(string entityName, string name, OperatingType operatingType)
{
EntityName = entityName;
Name = name;
OperateType = operatingType;
LogItems = new List<DataLogItem>();
} /// <summary>
/// 獲取或設(shè)置 類型名稱
/// </summary>
[StringLength()]
[Display(Name = "類型名稱")]
public string EntityName { get; set; } /// <summary>
/// 獲取或設(shè)置 實體名稱
/// </summary>
[Display(Name = "實體名稱")]
public string Name { get; set; } /// <summary>
/// 獲取或設(shè)置 數(shù)據(jù)編號
/// </summary>
[StringLength()]
[DisplayName("主鍵值")]
public string EntityKey { get; set; } /// <summary>
/// 獲取或設(shè)置 操作類型
/// </summary>
[Description("操作類型")]
public OperatingType OperateType { get; set; } /// <summary>
/// 獲取或設(shè)置 操作日志信息
/// </summary>
public virtual OperateLog OperateLog { get; set; } /// <summary>
/// 獲取或設(shè)置 操作明細(xì)
/// </summary>
public virtual ICollection<DataLogItem> LogItems { get; set; }
}
數(shù)據(jù)日志操作變更明細(xì)項
/// <summary>
/// 實體操作日志明細(xì)
/// </summary>
[Description("系統(tǒng)-操作日志明細(xì)信息")]
public class DataLogItem : EntityBase<Guid>
{
/// <summary>
/// 初始化一個<see cref="DataLogItem"/>類型的新實例
/// </summary>
public DataLogItem()
: this(null, null)
{ } /// <summary>
///初始化一個<see cref="DataLogItem"/>類型的新實例
/// </summary>
/// <param name="originalValue">舊值</param>
/// <param name="newValue">新值</param>
public DataLogItem(string originalValue, string newValue)
{
Id = CombHelper.NewComb();
OriginalValue = originalValue;
NewValue = newValue;
} /// <summary>
/// 獲取或設(shè)置 字段
/// </summary>
public string Field { get; set; } /// <summary>
/// 獲取或設(shè)置 字段名稱
/// </summary>
public string FieldName { get; set; } /// <summary>
/// 獲取或設(shè)置 舊值
/// </summary>
public string OriginalValue { get; set; } /// <summary>
/// 獲取或設(shè)置 新值
/// </summary>
public string NewValue { get; set; } /// <summary>
/// 獲取或設(shè)置 數(shù)據(jù)類型
/// </summary>
public string DataType { get; set; } /// <summary>
/// 獲取或設(shè)置 所屬數(shù)據(jù)日志
/// </summary>
public virtual DataLog DataLog { get; set; }
}
數(shù)據(jù)日志操作類型的枚舉:
/// <summary>
/// 實體數(shù)據(jù)日志操作類型
/// </summary>
public enum OperatingType
{
/// <summary>
/// 查詢
/// </summary>
Query = , /// <summary>
/// 新建
/// </summary>
Insert = , /// <summary>
/// 更新
/// </summary>
Update = , /// <summary>
/// 刪除
/// </summary>
Delete =
}
下圖以較直觀的方式顯示操作日志與數(shù)據(jù)日志之間的關(guān)系:
數(shù)據(jù)日志的創(chuàng)建
數(shù)據(jù)日志,主要記錄業(yè)務(wù)操作過程中涉及到的各個數(shù)據(jù)實體的變更,而這里的變更,主要是實體的新增、更新、刪除三種情況。
在EntityFramework的數(shù)據(jù)操作中,實體經(jīng)過業(yè)務(wù)處理之后,都是有狀態(tài)跟蹤的,即是 EntityState 枚舉類型:
public enum EntityState
{
Detached = ,
Unchanged = ,
Added = ,
Deleted = ,
Modified = ,
}
我們要關(guān)心的狀態(tài),主要是Added、Deleted、Modified三個值,分別對應(yīng)著新增、刪除、更新三種狀態(tài),在EntityFramework執(zhí)行到 SaveChanges 的時候,各個實體的狀態(tài)已經(jīng)確定。OSharp將在這個時機獲取變更的實體并創(chuàng)建數(shù)據(jù)日志信息。
/// <summary>
/// 提交當(dāng)前單元操作的更改
/// </summary>
/// <param name="validateOnSaveEnabled">提交保存時是否驗證實體約束有效性。</param>
/// <returns>操作影響的行數(shù)</returns>
internal virtual int SaveChanges(bool validateOnSaveEnabled)
{
bool isReturn = Configuration.ValidateOnSaveEnabled != validateOnSaveEnabled;
try
{
Configuration.ValidateOnSaveEnabled = validateOnSaveEnabled;
//記錄實體操作日志
13 List<DataLog> logs = new List<DataLog>();
14 if (DataLoggingEnabled)
15 {
16 logs = this.GetEntityDataLogs().ToList();
17 }
int count = base.SaveChanges();
19 if (count > 0 && DataLoggingEnabled)
20 {
21 Logger.Info(logs, true);
22 }
TransactionEnabled = false;
return count;
}
catch (DbUpdateException e)
{
if (e.InnerException != null && e.InnerException.InnerException is SqlException)
{
SqlException sqlEx = e.InnerException.InnerException as SqlException;
string msg = DataHelper.GetSqlExceptionMessage(sqlEx.Number);
throw new OSharpException("提交數(shù)據(jù)更新時發(fā)生異常:" + msg, sqlEx);
}
throw;
}
finally
{
if (isReturn)
{
Configuration.ValidateOnSaveEnabled = !validateOnSaveEnabled;
}
}
}
以上代碼中, DataLoggingEnabled 屬性 是當(dāng)前上下文是否開啟數(shù)據(jù)日志的總開關(guān),當(dāng)開啟數(shù)據(jù)日志記錄功能時,才進行數(shù)據(jù)日志的創(chuàng)建。
創(chuàng)建數(shù)據(jù)日志的實現(xiàn)如下,主要是從對象管理器中篩選出指定狀態(tài)的實體對象,再由實體類型全名獲取相應(yīng)實體的“實體信息記錄”,確定是否執(zhí)行數(shù)據(jù)日志的創(chuàng)建,然后創(chuàng)建數(shù)據(jù)日志信息:
/// <summary>
/// 獲取數(shù)據(jù)上下文的變更日志信息
/// </summary>
public static IEnumerable<DataLog> GetEntityDataLogs(this DbContext dbContext)
{
ObjectContext objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;
ObjectStateManager manager = objectContext.ObjectStateManager; IEnumerable<DataLog> logs = from entry in manager.GetObjectStateEntries(EntityState.Added).Where(entry => entry.Entity != null)
let entityInfo = OSharpContext.Current.EntityInfoHandler.GetEntityInfo(entry.Entity.GetType())
where entityInfo != null && entityInfo.DataLogEnabled
select GetAddedLog(entry, entityInfo); logs = logs.Concat(from entry in manager.GetObjectStateEntries(EntityState.Modified).Where(entry => entry.Entity != null)
let entityInfo = OSharpContext.Current.EntityInfoHandler.GetEntityInfo(entry.Entity.GetType())
where entityInfo != null && entityInfo.DataLogEnabled
select GetModifiedLog(entry, entityInfo)); logs = logs.Concat(from entry in manager.GetObjectStateEntries(EntityState.Deleted).Where(entry => entry.Entity != null)
let entityInfo = OSharpContext.Current.EntityInfoHandler.GetEntityInfo(entry.Entity.GetType())
where entityInfo != null && entityInfo.DataLogEnabled
select GetDeletedLog(entry, entityInfo)); return logs;
}
創(chuàng)建“新增”實體的數(shù)據(jù)日志:
/// <summary>
/// 獲取添加數(shù)據(jù)的日志信息
/// </summary>
/// <param name="entry">實體狀態(tài)跟蹤信息</param>
/// <param name="entityInfo">實體數(shù)據(jù)信息</param>
/// <returns>新增數(shù)據(jù)日志信息</returns>
private static DataLog GetAddedLog(ObjectStateEntry entry, IEntityInfo entityInfo)
{
DataLog log = new DataLog(entityInfo.ClassName, entityInfo.Name, OperatingType.Insert);
for (int i = ; i < entry.CurrentValues.FieldCount; i++)
{
string name = entry.CurrentValues.GetName(i);
if (name == "Timestamp")
{
continue;
}
object value = entry.CurrentValues.GetValue(i);
if (name == "Id")
{
log.EntityKey = value.ToString();
}
Type fieldType = entry.CurrentValues.GetFieldType(i);
DataLogItem logItem = new DataLogItem()
{
Field = name,
FieldName = entityInfo.PropertyNames[name],
NewValue = value == null ? null : value.ToString(),
DataType = fieldType == null ? null : fieldType.Name
};
log.LogItems.Add(logItem);
}
return log;
}
創(chuàng)建“更新”實體的數(shù)據(jù)日志:
/// <summary>
/// 獲取修改數(shù)據(jù)的日志信息
/// </summary>
/// <param name="entry">實體狀態(tài)跟蹤信息</param>
/// <param name="entityInfo">實體數(shù)據(jù)信息</param>
/// <returns>修改數(shù)據(jù)日志信息</returns>
private static DataLog GetModifiedLog(ObjectStateEntry entry, IEntityInfo entityInfo)
{
DataLog log = new DataLog(entityInfo.ClassName, entityInfo.Name, OperatingType.Update);
for (int i = ; i < entry.CurrentValues.FieldCount; i++)
{
string name = entry.CurrentValues.GetName(i);
if (name == "Timestamp")
{
continue;
}
object currentValue = entry.CurrentValues.GetValue(i);
object originalValue = entry.OriginalValues[name];
if (name == "Id")
{
log.EntityKey = originalValue.ToString();
}
if (currentValue.Equals(originalValue))
{
continue;
}
Type fieldType = entry.CurrentValues.GetFieldType(i);
DataLogItem logItem = new DataLogItem()
{
Field = name,
FieldName = entityInfo.PropertyNames[name],
NewValue = currentValue == null ? null : currentValue.ToString(),
OriginalValue = originalValue == null ? null : originalValue.ToString(),
DataType = fieldType == null ? null : fieldType.Name
};
log.LogItems.Add(logItem);
}
return log;
}
創(chuàng)建“刪除”實體的數(shù)據(jù)日志:
/// <summary>
/// 獲取刪除數(shù)據(jù)的日志信息
/// </summary>
/// <param name="entry">實體狀態(tài)跟蹤信息</param>
/// <param name="entityInfo">實體數(shù)據(jù)信息</param>
/// <returns>刪除數(shù)據(jù)日志信息</returns>
private static DataLog GetDeletedLog(ObjectStateEntry entry, IEntityInfo entityInfo)
{
DataLog log = new DataLog(entityInfo.ClassName, entityInfo.Name, OperatingType.Delete);
for (int i = ; i < entry.OriginalValues.FieldCount; i++)
{
string name = entry.OriginalValues.GetName(i);
if (name == "Timestamp")
{
continue;
}
object originalValue = entry.OriginalValues[i];
if (name == "Id")
{
log.EntityKey = originalValue.ToString();
}
Type fieldType = entry.OriginalValues.GetFieldType(i);
DataLogItem logItem = new DataLogItem()
{
Field = name,
FieldName = entityInfo.PropertyNames[name],
OriginalValue = originalValue == null ? null : originalValue.ToString(),
DataType = fieldType == null ? null : fieldType.Name
};
log.LogItems.Add(logItem);
}
return log;
}
數(shù)據(jù)日志的傳遞
前面我們已經(jīng)完成了數(shù)據(jù)日志創(chuàng)建,但數(shù)據(jù)日志是由數(shù)據(jù)層的EntityFramework的SaveChanges方法創(chuàng)建的,而創(chuàng)建的數(shù)據(jù)日志,最終將傳遞到上層定義的 OperateLogFilterAttribute 中進行使用,這就需要我們通過一定的機制將數(shù)據(jù)日志往上傳遞。在這里,使用的是日志組件。
OSharp中定義了一個數(shù)據(jù)日志緩存,專門用于接收數(shù)據(jù)層創(chuàng)建的數(shù)據(jù)日志信息:
/// <summary>
/// 數(shù)據(jù)日志緩存接口
/// </summary>
public interface IDataLogCache : IDependency
{
/// <summary>
/// 獲取 數(shù)據(jù)日志集合
/// </summary>
IEnumerable<DataLog> DataLogs { get; } /// <summary>
/// 向緩存中添加數(shù)據(jù)日志信息
/// </summary>
/// <param name="dataLog">數(shù)據(jù)日志信息</param>
void AddDataLog(DataLog dataLog);
}
在專用于數(shù)據(jù)日志記錄的 DatabaseLog 的 Write 方法重寫時,判斷數(shù)據(jù)是否是 DataLog 類型,并存入 IDataLogCache 中,這里使用MVC的依賴注入功能獲取IDataLogCache的實現(xiàn),以保證其在同一Http請求中,獲取的是同一實例:
/// <summary>
/// 獲取日志輸出處理委托實例
/// </summary>
/// <param name="level">日志輸出級別</param>
/// <param name="message">日志消息</param>
/// <param name="exception">日志異常</param>
/// <param name="isData">是否數(shù)據(jù)日志</param>
protected override void Write(LogLevel level, object message, Exception exception, bool isData = false)
{
if (!isData)
{
return;
}
IEnumerable<DataLog> dataLogs = message as IEnumerable<DataLog>;
if (dataLogs == null)
{
return;
}
IDataLogCache logCache = DependencyResolver.Current.GetService<IDataLogCache>();
foreach (DataLog dataLog in dataLogs)
{
logCache.AddDataLog(dataLog);
}
}
操作日志的記錄
定義了一個 OperateLogFilterAttribute 的ActionFilter,專門用于攔截并記錄操作日志。
/// <summary>
/// 操作日志記錄過濾器
/// </summary>
public class OperateLogFilterAttribute : ActionFilterAttribute
{
/// <summary>
/// 獲取或設(shè)置 數(shù)據(jù)日志緩存
/// </summary>
public IDataLogCache DataLogCache { get; set; } /// <summary>
/// 獲取或設(shè)置 操作日志輸出者
/// </summary>
public IOperateLogWriter OperateLogWriter { get; set; } /// <summary>
/// Called after the action method executes.
/// </summary>
/// <param name="filterContext">The filter context.</param>
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
string area = filterContext.GetAreaName();
string controller = filterContext.GetControllerName();
string action = filterContext.GetActionName(); IFunction function = OSharpContext.Current.FunctionHandler.GetFunction(area, controller, action);
if (function == null || !function.OperateLogEnabled)
{
return;
}
Operator @operator = new Operator()
{
Ip = filterContext.HttpContext.Request.GetIpAddress(),
};
if (filterContext.HttpContext.Request.IsAuthenticated)
{
ClaimsIdentity identity = filterContext.HttpContext.User.Identity as ClaimsIdentity;
if (identity != null)
{
@operator.UserId = identity.GetClaimValue(ClaimTypes.NameIdentifier);
@operator.Name = identity.GetClaimValue(ClaimTypes.Name);
@operator.NickName = identity.GetClaimValue(ClaimTypes.GivenName);
}
} OperateLog operateLog = new OperateLog()
{
FunctionName = function.Name,
Operator = @operator
};
if (function.DataLogEnabled)
{
foreach (DataLog dataLog in DataLogCache.DataLogs)
{
operateLog.DataLogs.Add(dataLog);
}
}
OperateLogWriter.Write(operateLog);
}
}
最后,操作日志將由 IOperateLogWriter 進行輸出,定義如下:
/// <summary>
/// 操作日志輸出接口
/// </summary>
public interface IOperateLogWriter : IDependency
{
/// <summary>
/// 輸出操作日志
/// </summary>
/// <param name="operateLog">操作日志信息</param>
void Write(OperateLog operateLog);
}
默認(rèn)的,操作日志將被記錄到數(shù)據(jù)庫中:
/// <summary>
/// 操作日志數(shù)據(jù)庫輸出實現(xiàn)
/// </summary>
public class DatabaseOperateLogWriter : IOperateLogWriter
{
private readonly IRepository<OperateLog, int> _operateLogRepository; /// <summary>
/// 初始化一個<see cref="DatabaseOperateLogWriter"/>類型的新實例
/// </summary>
public DatabaseOperateLogWriter(IRepository<OperateLog, int> operateLogRepository)
{
_operateLogRepository = operateLogRepository;
} /// <summary>
/// 輸出操作日志
/// </summary>
/// <param name="operateLog">操作日志信息</param>
public void Write(OperateLog operateLog)
{
operateLog.CheckNotNull("operateLog" );
_operateLogRepository.Insert(operateLog);
}
}
操作日志顯示
如果一條操作日志中包含有數(shù)據(jù)日志,那么數(shù)據(jù)日志將以下級數(shù)據(jù)的方式展現(xiàn)在操作日志中:
開源說明
github.com
OSharp項目已在github.com上開源,地址為:https://github.com/i66soft/osharp,歡迎閱讀代碼,歡迎 Watch(關(guān)注),歡迎 Star(推薦),如果您認(rèn)同 OSharp 項目的設(shè)計思想,歡迎參與 OSharp 項目的開發(fā)。
在Visual Studio 2013中,可直接獲取 OSharp 的最新源代碼,獲取方式如下,地址為:https://github.com/i66soft/osharp.git
開源項目參與方式
很多童鞋想?yún)⑴c開源項目,為項目做貢獻,但又不知道如何做,這里我簡單說下參與OSharp的步驟吧:
- 在 https://github.com/i66soft/osharp 右上角 Fork 一下項目源碼,在你的賬戶下會有一份代碼的副本
- 使用VisualStudio Clone 你賬戶下的代碼到本地,更改代碼,再提交,就完成代碼的更改了
- 如果覺得有并入 i66soft 主干的價值,可以向主干提交 pull request申請,如果我審核通過,就可以合并到主干了,這就形成了一次開源代碼的貢獻了
- 如果我沒有接受合并,你也可以在你的賬戶上按你的風(fēng)格去發(fā)展osharp
- 我也會經(jīng)常瀏覽各個Fork版本對項目的更改,如果覺得有價值,也會主動合并到主干代碼中,也能形成一次對開源的貢獻
- 為保證提交的質(zhì)量,也便于對代碼的合并,每次更改與提交應(yīng)該只做一件事,只提交必要的更改
nuget
OSharp的相關(guān)類庫已經(jīng)發(fā)布到nuget上,歡迎試用,直接在nuget上搜索 “osharp” 關(guān)鍵字即可找到
系列導(dǎo)航
本文已同步到系列目錄:OSharp快速開發(fā)框架解說系列
總結(jié)
以上是生活随笔為你收集整理的【开源】OSharp3.0框架解说系列(6.2):操作日志与数据日志的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: log4j 使用记录
- 下一篇: 康拓展开-排列的hash