SQL Server日志分析程序开发思路

本文涉及的产品
云数据库 RDS SQL Server,独享型 2核4GB
简介: 原文http://www.handytech.cn/blog/article.asp?id=126用过logExplorer的朋友都会被他强悍的功能吸引,我写过一篇详细的操作文档可以参考http://blog.

原文http://www.handytech.cn/blog/article.asp?id=126

用过logExplorer的朋友都会被他强悍的功能吸引,我写过一篇详细的操作文档可以参考
http: // blog.csdn.net/jinjazz/archive/2008/05/19/2459692.aspx

我们可以自己用开发工具来实现sql日志的读取,这个应用还是很酷的,具体思路

1、首先要了解一个没有公开的系统函数::fn_dblog,他可以读取sql日志,并返回二进制的行数据
2、然后要了解sql的二进制数据是如何存储的,这个可以参考我的blog文章
http: // blog.csdn.net/jinjazz/archive/2008/08/07/2783872.aspx
3、用自己擅长的开发工具来分析数据,得到我们需要的信息

我用c#写了一个测试样例,分析了int, char,datetime和varchar的日志情况而且没有考虑null和空字符串的保存,希望感兴趣的朋友能和我一起交流打造属于自己的日志分析工具

详细的试验步骤以及代码如下:

1、首先建立sqlserver的测试环境,我用的sql2005,这个过程不能保证在之前的版本中运行
以下sql语句会建立一个dbLogTest数据库,并建立一张log_test表,然后插入3条数据之后把表清空
 

use master 
go 
create database dbLogTest 
go 
use  dbLogTest 
go 
create table log_test(id  int ,code  char( 10),name varchar( 20),date datetime,memo varchar( 100)) 
insert into log_test  select  100' id001 ', ' jinjazz ',getdate(), ' 剪刀 ' 
insert into log_test  select  65549, ' id002 ', ' 游客 ',getdate()- 1, ' 这家伙很懒,没有设置昵称 ' 
insert into log_test  select - 999, ' id003 ', ' 这家伙来自火星 ',getdate()- 1000, ' a ' 
  
delete  from log_test 
  
--use master  
--go 
--drop database dbLogTest 
  

2、我们最终的目的是要找到被我们删掉的数据

3、分析日志的c#代码:我已经尽量详细的写了注释
 

using System; 
using System.Collections.Generic; 
using System.Text; 
  
namespace ConsoleApplication21 

     class Program 
    { 
         ///   <summary>  
        
///  分析sql2005日志,找回被delete的数据,引用请保留以下信息 
        
///  作者:jinjazz (csdn的剪刀) 
        
///  作者blog: http://blog.csdn.net/jinjazz  
        
///   </summary>  
        
///   <param name="args"></param>  
         static  void Main( string[] args) 
        { 
             using (System.Data.SqlClient.SqlConnection conn =  new System.Data.SqlClient.SqlConnection()) 
            { 
                conn.ConnectionString =  " server=localhost;uid=sa;pwd=sqlgis;database=dbLogTest "
                conn.Open(); 
                 using (System.Data.SqlClient.SqlCommand command = conn.CreateCommand()) 
                { 
                     // 察看dbo.log_test对象的sql日志 
                    command.CommandText =  @" Select allocunitname,operation,[RowLog Contents 0] as r0,[RowLog Contents 1]as r1  
                                from::fn_dblog (null, null)    
                                where allocunitname like 'dbo.log_test%'and 
                                operation in('LOP_Insert_ROWS','LOP_Delete_ROWS')
"
  
                    System.Data.SqlClient.SqlDataReader reader = command.ExecuteReader(); 
                     // 根据表字段的顺序建立字段数组 
                    Datacolumn[] columns =  new Datacolumn[] 
                        { 
                             new Datacolumn( " id ", System.Data.SqlDbType.Int), 
                             new Datacolumn( " code ", System.Data.SqlDbType.Char, 10), 
                             new Datacolumn( " name ", System.Data.SqlDbType.VarChar), 
                             new Datacolumn( " date ", System.Data.SqlDbType.DateTime), 
                             new Datacolumn( " memo ", System.Data.SqlDbType.VarChar) 
                        }; 
                     // 循环读取日志 
                     while (reader.Read()) 
                    { 
                         byte[] data = ( byte[])reader[ " r0 "]; 
                         
                         try 
                        { 
                             // 把二进制数据结构转换为明文 
                            TranslateData(data, columns); 
                            Console.WriteLine( " 数据对象{1}的{0}操作: ", reader[ " operation "], reader[ " allocunitname "]); 
                             foreach (Datacolumn c  in columns) 
                            { 
                                Console.WriteLine( " {0} = {1} ", c.Name, c.Value); 
                            } 
                            Console.WriteLine(); 
                        } 
                         catch 
                        { 
                             // to-do... 
                        } 
                         
                    } 
                    reader.Close(); 
                } 
                conn.Close(); 
            } 
            Console.WriteLine( " ************************日志分析完成 "); 
            Console.ReadLine(); 
        } 
         // 自定义的column结构 
         public  class Datacolumn 
        { 
             public  string Name; 
             public System.Data.SqlDbType DataType; 
             public  short Length = - 1
             public  object Value =  null
             public Datacolumn( string name, System.Data.SqlDbType type) 
            { 
                Name = name; 
                DataType = type; 
            } 
             public Datacolumn( string name,System.Data.SqlDbType type, short length) 
            { 
                Name = name; 
                DataType = type; 
                Length = length; 
            } 
        } 
         ///   <summary>  
        
///  sql二进制结构翻译,这个比较关键,测试环境为sql2005,其他版本没有测过。 
        
///   </summary>  
        
///   <param name="data"></param>  
        
///   <param name="columns"></param>  
         static  void TranslateData( byte[] data, Datacolumn[] columns) 
        { 
             // 我只根据示例写了Char,DateTime,Int三种定长度字段和varchar一种不定长字段,其余的有兴趣可以自己补充 
            
// 这里没有暂时没有考虑Null和空字符串两种情况,以后会补充。 
  
            
// 引用请保留以下信息: 
            
// 作者:jinjazz  
            
// sql的数据行二进制结构参考我的blog 
            
// http://blog.csdn.net/jinjazz/archive/2008/08/07/2783872.aspx  
            
// 行数据从第5个字节开始 
             short index =  4
             // 先取定长字段 
             foreach (Datacolumn c  in columns) 
            { 
                 switch (c.DataType) 
                { 
                     case System.Data.SqlDbType.Char: 
                         // 读取定长字符串,需要根据表结构指定长度 
                        c.Value = System.Text.Encoding.Default.GetString(data,index,c.Length); 
                        index += c.Length; 
                         break
                     case System.Data.SqlDbType.DateTime: 
                         // 读取datetime字段,sql为8字节保存 
                        System.DateTime date =  new DateTime( 190011); 
                         // 前四位1/300秒保存 
                         int second = BitConverter.ToInt32(data, index); 
                        date = date.AddSeconds(second/ 300); 
                        index +=  4
                         // 后四位1900-1-1的天数 
                         int days = BitConverter.ToInt32(data, index); 
                        date=date.AddDays(days); 
                        index +=  4
                        c.Value = date; 
                         break
                     case System.Data.SqlDbType.Int: 
                         // 读取int字段,为4个字节保存 
                        c.Value = BitConverter.ToInt32(data, index); 
                        index +=  4
                         break
                    default
                        // 忽略不定长字段和其他不支持以及不愿意考虑的字段 
                         break
                } 
            } 
             // 跳过三个字节 
            index +=  3
             // 取变长字段的数量,保存两个字节 
             short varColumnCount = BitConverter.ToInt16(data, index); 
            index +=  2
             // 接下来,每两个字节保存一个变长字段的结束位置, 
            
// 所以第一个变长字段的开始位置可以算出来 
             short startIndex =( short)( index + varColumnCount *  2); 
             // 第一个变长字段的结束位置也可以算出来 
             short endIndex = BitConverter.ToInt16(data, index); 
             // 循环变长字段列表读取数据 
             foreach (Datacolumn c  in columns) 
            { 
                 switch (c.DataType) 
                { 
                     case System.Data.SqlDbType.VarChar: 
                         // 根据开始和结束位置,可以算出来每个变长字段的值 
                        c.Value =System.Text.Encoding.Default.GetString(data, startIndex, endIndex - startIndex); 
                         // 下一个变长字段的开始位置 
                        startIndex = endIndex; 
                         // 获取下一个变长字段的结束位置 
                        index +=  2
                        endIndex = BitConverter.ToInt16(data, index); 
                         break
                     default
                         // 忽略定长字段和其他不支持以及不愿意考虑的字段 
                         break
                } 
            } 
             // 获取完毕 
        } 
    } 

  
4、更改你的sql连接字符串后运行以上代码,会看到如下输出信息:
 

数据对象dbo.log_test的LOP_Insert_ROWS操作: 
id =  100 
code = id001 
name = jinjazz 
date =  2008- 8- 7  18: 14: 03 
memo = 剪刀 
  
数据对象dbo.log_test的LOP_Insert_ROWS操作: 
id =  65549 
code = id002 
name = 游客 
date =  2008- 8- 6  18: 14: 03 
memo = 这家伙很懒,没有设置昵称 
  
数据对象dbo.log_test的LOP_Insert_ROWS操作: 
id = - 999 
code = id003 
name = 这家伙来自火星 
date =  2005- 11- 11  18: 14: 03 
memo = a 
  
数据对象dbo.log_test的LOP_Delete_ROWS操作: 
id =  100 
code = id001 
name = jinjazz 
date =  2008- 8- 7  18: 14: 03 
memo = 剪刀 
  
数据对象dbo.log_test的LOP_Delete_ROWS操作: 
id =  65549 
code = id002 
name = 游客 
date =  2008- 8- 6  18: 14: 03 
memo = 这家伙很懒,没有设置昵称 
  
数据对象dbo.log_test的LOP_Delete_ROWS操作: 
id = - 999 
code = id003 
name = 这家伙来自火星 
date =  2005- 11- 11  18: 14: 03 
memo = a 
  
************************日志分析完成 

 

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
8天前
|
SQL 人工智能 算法
【SQL server】玩转SQL server数据库:第二章 关系数据库
【SQL server】玩转SQL server数据库:第二章 关系数据库
51 10
|
1月前
|
SQL 数据库 数据安全/隐私保护
Sql Server数据库Sa密码如何修改
Sql Server数据库Sa密码如何修改
|
18天前
|
SQL
启动mysq异常The server quit without updating PID file [FAILED]sql/data/***.pi根本解决方案
启动mysq异常The server quit without updating PID file [FAILED]sql/data/***.pi根本解决方案
16 0
|
8天前
|
SQL 算法 数据库
【SQL server】玩转SQL server数据库:第三章 关系数据库标准语言SQL(二)数据查询
【SQL server】玩转SQL server数据库:第三章 关系数据库标准语言SQL(二)数据查询
67 6
|
3天前
|
SQL 安全 网络安全
IDEA DataGrip连接sqlserver 提示驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接的解决方法
IDEA DataGrip连接sqlserver 提示驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接的解决方法
8 0
|
8天前
|
SQL 存储 数据挖掘
数据库数据恢复—RAID5上层Sql Server数据库数据恢复案例
服务器数据恢复环境: 一台安装windows server操作系统的服务器。一组由8块硬盘组建的RAID5,划分LUN供这台服务器使用。 在windows服务器内装有SqlServer数据库。存储空间LUN划分了两个逻辑分区。 服务器故障&初检: 由于未知原因,Sql Server数据库文件丢失,丢失数据涉及到3个库,表的数量有3000左右。数据库文件丢失原因还没有查清楚,也不能确定数据存储位置。 数据库文件丢失后服务器仍处于开机状态,所幸没有大量数据写入。 将raid5中所有磁盘编号后取出,经过硬件工程师检测,没有发现明显的硬件故障。以只读方式将所有磁盘进行扇区级的全盘镜像,镜像完成后将所
数据库数据恢复—RAID5上层Sql Server数据库数据恢复案例
|
12天前
|
SQL 数据安全/隐私保护
SQL Server 2016安装教程
SQL Server 2016安装教程
17 1
|
12天前
|
SQL 安全 Java
SQL server 2017安装教程
SQL server 2017安装教程
14 1
|
25天前
|
SQL 存储 Python
Microsoft SQL Server 编写汉字转拼音函数
Microsoft SQL Server 编写汉字转拼音函数
|
26天前
|
SQL JSON Kubernetes
Seata常见问题之服务端 error日志没有输出,客户端执行sql报错如何解决
Seata 是一个开源的分布式事务解决方案,旨在提供高效且简单的事务协调机制,以解决微服务架构下跨服务调用(分布式场景)的一致性问题。以下是Seata常见问题的一个合集
97 0