前言:

  近日在项目协同开发过程中出现了问题,数据出现了异常;其他人员怀疑项目数据丢失程序存在问题。于是通过排查程序提供的审计日志最终还原了当时操作及原因。

  可见审计日志在排查、定位问题是相当有用的,那么在.Net Core 如何来实现审计日志呢?

  接下来一步步来实现效果

一、审计日志定义及作用

 审计日志:  

维基百科: “审计跟踪(也叫审计日志)是与安全相关的按照时间顺序的记录,记录集或者记录源,它们提供了活动序列的文档证据,这些活动序列可以在任何时间影响一个特定的操作,步骤或其他”

 作用:

  1、快速定位问题耗时及性能情况

  2、记录调用时环境信息:如浏览器、参数等

二、.Net Core 中实现审计日志

 那么怎么实现审计日志呢?其实核心思想很简单。包含以下步骤:

  • 获取调用接口方法时相关信息
  • 记录当前接口耗时情况
  • 保存审计日志信息到数据库中

  那么如何获取调用接口时相关信息呢?.Net Core中可以使用:过滤器、拦截器 实现。 

  本次示例中将采用过滤器实现审计日志实现功能;主要流程如下

  • 定义审计日志信息:  

    public class AuditInfo
    {
    /// <summary>
    /// 调用参数
    /// </summary>
    public string Parameters { get; set; }
    /// <summary>
    /// 浏览器信息
    /// </summary>
    public string BrowserInfo { get; set; }
    /// <summary>
    /// 客户端信息
    /// </summary>
    public string ClientName { get; set; }
    /// <summary>
    /// 客户端IP地址
    /// </summary>
    public string ClientIpAddress { get; set; }
    /// <summary>
    /// 执行耗时
    /// </summary>
    public int ExecutionDuration { get; set; }
    /// <summary>
    /// 执行时间
    /// </summary>
    public DateTime ExecutionTime { get; set; }
    /// <summary>
    /// 返回内容
    /// </summary>
    public string ReturnValue { get; set; }
    /// <summary>
    /// 异常对象
    /// </summary>
    public Exception Exception { get; set; }
    /// <summary>
    /// 方法名
    /// </summary>
    public string MethodName { get; set; }
    /// <summary>
    /// 服务名
    /// </summary>
    public string ServiceName { get; set; }
    /// <summary>
    /// 调用者信息
    /// </summary>
    public string UserInfo { get; set; }
    /// <summary>
    /// 自定义数据
    /// </summary>
    public string CustomData { get; set; }
    }
  • 实现审计日志过滤器

    using AuditLogDemo.Models;
    using AuditLogDemo.Services;
    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.Controllers;
    using Microsoft.AspNetCore.Mvc.Filters;
    using Microsoft.Extensions.Logging;
    using Newtonsoft.Json;
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Reflection;
    using System.Threading.Tasks; namespace AuditLogDemo.Fliters
    {
    public class AuditLogActionFilter : IAsyncActionFilter
    {
    /// <summary>
    /// 审计日志服务对象
    /// </summary>
    private readonly IAuditLogService _auditLogService;
    /// <summary>
    /// 登录用户
    /// </summary>
    private readonly ISession _Session;
    /// <summary>
    /// 日志记录
    /// </summary>
    private readonly ILogger<AuditLogActionFilter> _logger; public AuditLogActionFilter(
    IAuditLogService auditLogService,
    ISession Session,
    ILogger<AuditLogActionFilter> logger
    )
    {
    _Session = Session;
    _logger = logger;
    _auditLogService = auditLogService;
    } public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
    // 判断是否写日志
    if (!ShouldSaveAudit(context))
    {
    await next();
    return;
    }
    //接口Type
    var type = (context.ActionDescriptor as ControllerActionDescriptor).ControllerTypeInfo.AsType();
    //方法信息
    var method = (context.ActionDescriptor as ControllerActionDescriptor).MethodInfo;
    //方法参数
    var arguments = context.ActionArguments;
    //开始计时
    var stopwatch = Stopwatch.StartNew();
    var auditInfo = new AuditInfo
    {
    UserInfo = _Session?.Id,
    ServiceName = type != null ? type.FullName.TruncateWithPostfix(EntityDefault.FieldsLength250) : "",
    MethodName = method.Name.TruncateWithPostfix(EntityDefault.FieldsLength250),
    ////请求参数转Json
    Parameters = JsonConvert.SerializeObject(arguments),
    ExecutionTime = DateTime.Now,
    BrowserInfo = context.HttpContext.Request.Headers["User-Agent"].ToString().TruncateWithPostfix(EntityDefault.FieldsLength250),
    ClientIpAddress = context.HttpContext.Connection.RemoteIpAddress.ToString().TruncateWithPostfix(EntityDefault.FieldsLength50),
    //ClientName = _clientInfoProvider.ComputerName.TruncateWithPostfix(EntityDefault.FieldsLength100),
    Id = Guid.NewGuid().ToString()
    }; ActionExecutedContext result = null;
    try
    {
    result = await next();
    if (result.Exception != null && !result.ExceptionHandled)
    {
    auditInfo.Exception = result.Exception;
    }
    }
    catch (Exception ex)
    {
    auditInfo.Exception = ex;
    throw;
    }
    finally
    {
    stopwatch.Stop();
    auditInfo.ExecutionDuration = Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds); if (result != null)
    {
    switch (result.Result)
    {
    case ObjectResult objectResult:
    auditInfo.ReturnValue = JsonConvert.SerializeObject(objectResult.Value);
    break; case JsonResult jsonResult:
    auditInfo.ReturnValue = JsonConvert.SerializeObject(jsonResult.Value);
    break; case ContentResult contentResult:
    auditInfo.ReturnValue = contentResult.Content;
    break;
    }
    }
    Console.WriteLine(auditInfo.ToString());
    //保存审计日志
    await _auditLogService.SaveAsync(auditInfo);
    }
    } /// <summary>
    /// 是否需要记录审计
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    private bool ShouldSaveAudit(ActionExecutingContext context)
    {
    if (!(context.ActionDescriptor is ControllerActionDescriptor))
    return false;
    var methodInfo = (context.ActionDescriptor as ControllerActionDescriptor).MethodInfo; if (methodInfo == null)
    {
    return false;
    } if (!methodInfo.IsPublic)
    {
    return false;
    } if (methodInfo.GetCustomAttribute<AuditedAttribute>() != null)
    {
    return true;
    } if (methodInfo.GetCustomAttribute<DisableAuditingAttribute>() != null)
    {
    return false;
    } var classType = methodInfo.DeclaringType;
    if (classType != null)
    {
    if (classType.GetTypeInfo().GetCustomAttribute<AuditedAttribute>() != null)
    {
    return true;
    } if (classType.GetTypeInfo().GetCustomAttribute<AuditedAttribute>() != null)
    {
    return false;
    }
    }
    return false;
    }
    }
    }

   该内容为实现审计日志功能主要逻辑,通过过滤器获取当前执行控制器、方法判断是否需要记录审计日志;其他请求参数、客户端ip等相关基本信息组成审计日志对象,并记录调用时间。 

  • 注册过滤器

    public void ConfigureServices(IServiceCollection services)
    {
    services.AddControllers(options =>
    {
    options.Filters.Add(typeof(AuditLogActionFilter));
    }); //审计日志存储
    services.AddDbContext<AuditLogDBContent>(options =>
    {
    string conn = Configuration.GetConnectionString("LogDB");
    options.UseSqlite(conn);
    });
    }

  到此审计日志主要逻辑已经实现完成。是不是很简单

三、总结

 回过头来看,在.net core 中需要统一监控或过滤时,可以采用过滤器(Filter)或拦截器来实现相关效果

 .Net Core中 Filter 常件的有:Authorization Filter(认证过滤器),Resource Filter(资源过滤器),Exception Filter(异常过滤器),Action Filter(方法过滤器),Result Filter(结果过滤器)

 

 

.Net Core 审计日志实现的更多相关文章

  1. ABP文档 - 审计日志

    文档目录 本节内容: 简介 关于 IAuditingStore 配置 通过特性启用/禁用 注意 简介 维基百科:“一个审计追踪(也叫审计日志)是一个安全相关的时序记录.记录组.和/或记录源和目标,作为 ...

  2. ABP官方文档翻译 4.6 审计日志

    审计日志 介绍 关于IAuditingStore 配置 通过特性启用/禁用 注意事项 介绍 维基百科:“审计追踪(也称为审计日志)是与安全相关的按时间先后的记录.记录集合.记录的目的地和源,提供一系列 ...

  3. .NET Core的日志[5]:利用TraceSource写日志

    从微软推出第一个版本的.NET Framework的时候,就在“System.Diagnostics”命名空间中提供了Debug和Trace两个类帮助我们完成针对调试和跟踪信息的日志记录.在.NET ...

  4. .NET Core的日志[4]:将日志写入EventLog

    面向Windows的编程人员应该不会对Event Log感到陌生,以至于很多人提到日志,首先想到的就是EventLog.EventLog不仅仅记录了Windows系统自身针对各种事件的日志,我们的应用 ...

  5. .NET Core的日志[3]:将日志写入Debug窗口

    定义在NuGet包"Microsoft.Extensions.Logging.Debug"中的DebugLogger会直接调用Debug的WriteLine方法来写入分发给它的日志 ...

  6. .NET Core的日志[2]:将日志输出到控制台

    对于一个控制台应用,比如采用控制台应用作为宿主的ASP.NET Core应用,我们可以将记录的日志直接输出到控制台上.针对控制台的Logger是一个类型为ConsoleLogger的对象,Consol ...

  7. .NET Core的日志[1]:采用统一的模式记录日志

    记录各种级别的日志是所有应用不可或缺的功能.关于日志记录的实现,我们有太多第三方框架可供选择,比如Log4Net.NLog.Loggr和Serilog 等,当然我们还可以选择微软原生的诊断框架(相关A ...

  8. ABP(现代ASP.NET样板开发框架)系列之19、ABP应用层——审计日志

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之19.ABP应用层——审计日志 ABP是“ASP.NET Boilerplate Project (ASP.NET ...

  9. ABP理论学习之审计日志

    返回总目录 本篇目录 介绍 配置 通过特性开启/关闭 注意 我项目中的例子 介绍 维基百科说: "审计跟踪(也叫审计日志)是与安全相关的按照时间顺序的记录,记录集或者记录源,它们提供了活动序 ...

随机推荐

  1. c语言版单链表

    1 //c语言单链表 2 #include <stdio.h> 3 #include <stdlib.h> 4 typedef struct Node 5 { 6 int da ...

  2. 测试中:ANR是什么

    1.ANR 的定义 ANR(Application Not Responding),用户可以选择"等待"而让程序继续运行,也可以选择"强制关闭".所以一个流畅的 ...

  3. golang 自学系列(三)—— if,for,channel

    golang 自学系列(三)-- if,for,channel 一般情况下,if 语句跟大多数语言的 if 判断语句一样,根据一个 boolean 表达式结果来执行两个分支逻辑. 但凡总是有例外,go ...

  4. PyQt(Python+Qt)学习随笔:Model/View架构概述

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 一.引言 模型-视图-控制器(Model-View-Controller,简称MVC)是一种源于Sm ...

  5. Jmeter录制脚本-BadBoy

    录制脚本方式: 1)Badboy录制Jmter脚本 2)Jmter代理服务录制脚本 方式一:Badboy录制脚本 安装Badboy 录制脚本 1.双击Badboy 2.输入URL 3.点击" ...

  6. CSP-S2020 浙江 游记

    2020.10.9 今天是 \(2020\) 年 \(10\) 月 \(9\) 日,距离初赛还有两天(算两天吗,完整的应该只有一天多了). 原本对于比赛还是没什么感觉的,每天做做题,水水文章,感觉时间 ...

  7. 数据结构—— Trie (前缀树)

    实现一个 Trie (前缀树),包含 插入, 查询, 和 查询前缀这三个操作. Trie trie = new Trie(); trie.insert("apple"); trie ...

  8. c++ 解析yaml文件

    一直用c++操作ini做配置文件,想换成yaml,在全球最大的同性交友网站github上搜索,看有没有开源的库,功夫不负有心人,找到了yaml-cpp,用他解析了一个yaml的例子非常好使,分享一下如 ...

  9. 性能测试工具 jmeter 分布式压力测试实操

    性能测试工具 jmeter 分布式压力测试实操 本文在Non-GUI Mode下进行,准备好三台有jdk环境,linux操作系统,同一局域网测试机器,运行两台slave,一台master机器,进行分布 ...

  10. linux环境下jdk安装以及配置

    linux 环境安装jdk和配置环境变量: (此处以root用户安装,此方式安装一台虚拟机装一个jdk即可,所有普通用户可以共用) 1.下载安装jdk 链接: https://pan.baidu.co ...