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

原文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(1900, 1, 1); 
                        //前四位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 
  
************************日志分析完成 

 

时间: 2024-10-28 04:57:39

SQL Server日志分析程序开发思路的相关文章

开发连接远程SQL Server 的ASP程序应注意的问题

Microsoft SQL Server7.0是一种大型.分布式数据库系统,也可被认为是一种大型.分布式数据仓库.由于其与Windows NT和BackOffice以及Visual Studio具有紧密的配合,已被越来越多的网络应用系统所采用.而ASP程序也以其和NT的紧密连接,可使用ActiveX对象.CGI接口对象化等特点向CGI程序发起了有力的挑战.下面,就谈谈开发连接远程SQL Server 的ASP程序应注意的几个问题. SQL Server是一个十分注重安全性的数据库,特别是在7.0

SQL Server 2005分析服务 统一整合视图

统一的空间模型 分析服务提供了一个商务语义模型,被称为统一的空间模型 (UDM).该模型定义了商务实体.商务逻辑.计算和度量.UDM 是一个中心位置,充当所有报告.电子表格.OLAP浏览器.KIPS 和分析应用程序的真实情况的唯一版本. 使用强大的新的数据源视图功能,可以将UDM映射为后端不同类型数据源的主机,提供完全整合了的商务图像,而与数据的位置无关.通过使用友好的商务实体描述.层次导航.多视图以及自动翻译为当地语言的功能,用户将会发现可以轻松浏览企业的商务数据. •主动的缓冲 主动的缓冲使

SQL SERVER日志链简介

先说清楚这些概念吧 SQLSERVER只有日志链,备份记录(有些人也叫备份链)本人觉得叫备份记录更合适 下面三个东西说的都是同一样东西 备份集=备份记录=备份链 备份集:比如备份的集合,比如有对一个数据库的完备1.差备.日备1.完备2.日备2,这些数据库的备份的集合就 是备份集 不过我更喜欢叫备份记录 备份记录实际上指 SELECT * FROM [msdb].[dbo].[backupset] 截断日志跟日志链断裂是否是同一样东西? 截断日志跟日志链断裂不是同一样东西 什么是日志链 其实大家可

浅析三线程程序开发思路与实现

一.前言 中国黑客(worm.runouce)病毒在国内出现以后,各大反病毒公司都对其进行了"仔细"的分析,得出一个结论:"中国黑客"发现了全球首创的"三线程"结构.这是某公司对外的宣传词,我个人对病毒没什么研究,并且我对worm.runouce没有任何的个人看法,不过我可以确信的是很多反病毒公司往往在夸大事实,目的只有一个:让更多的用户觉得某某病毒很可怕,让更多的用户相信只有某某公司的杀毒软件才可以彻底将病毒清除掉.其实三线程并没有好高深的技术

使用SQL Server日志转移实现数据库的高可用性

[导读]本文主要针对SQL Server 2000介绍如何使用SQL Server日志转移实现数据库的高可用性. 集群是一种实现高可用性的有效解决方案,有时它会适得其反.而且,它还非常昂贵.因此,数据库管理员可使用日志转移代替集群来提供较高的可用性. 日志转移是这样一种处理过程,它能将某一数据库中的事务日志文件依次转存到备份的数据库中,进而为这一数据库创建一个"近乎"热备份.SQL Server 2000的数据库引擎中设置了日志转移功能,并在其中进行处理.所以它会自动完成复原到备份服务

SQL Server日志清空方法 .

SQL Server日志清空方法 . 查询分析器中顺序执行以下三步,其中   databasename   为你的数据库教程文件名 1.清空日志:DUMP   TRANSACTION   databasename   WITH   NO_LOG   2.截断事务日志:BACKUP   LOG   databasename   WITH   NO_LOG   3.收缩数据库:DBCC   SHRINKDATABASE(databasename)   --//////////////////////

SQL Server日志过大会影响查询结果_MsSql

前几天发现网站的查询有点不正常,一般来说,会先查询出一级目录的内容,单击一级目录后会查询出二级目录--一直到查询到最下面的文件.可是这几天发现,查询速度很慢,而且,查询出来的也不是一级目录,而是直接显示最下面的文件,显示的数据量也有问题. 一开始还以为是网站被人黑了,因为网站没有修改过,所以应该不是网站自身的问题. 在服务器上查了半天,什么也没有查出来. 后来去看网站程序,看了半天,也没有看出什么问题来. 再后来去看数据库,看了半天,同样没有看出什么问题来. 最后无意中看到数据库的日志文件很大,

SQL Server日志过大会影响查询结果

前几天发现网站的查询有点不正常,一般来说,会先查询出一级目录的内容,单击一级目录后会查询出二级目录--一直到查询到最下面的文件.可是这几天发现,查询速度很慢,而且,查询出来的也不是一级目录,而是直接显示最下面的文件,显示的数据量也有问题. 一开始还以为是网站被人黑了,因为网站没有修改过,所以应该不是网站自身的问题. 在服务器上查了半天,什么也没有查出来. 后来去看网站程序,看了半天,也没有看出什么问题来. 再后来去看数据库,看了半天,同样没有看出什么问题来. 最后无意中看到数据库的日志文件很大,

SQL SERVER日志清除的两种方法

server SQL SERVER日志清除的两种方法方法一 一般情况下,SQL数据库的收缩并不能很大程度上减小数据库大小,其主要作用是收缩日志大小,应当定期进行此操作以免数据库日志过大1.设置数据库模式为简单模式:打开SQL企业管理器,在控制台根目录中依次点开Microsoft SQL Server-->SQL Server组-->双击打开你的服务器-->双击打开数据库目录-->选择你的数据库名称(如论坛数据库Forum)-->然后点击右键选择属性-->选择选项--&g