在上一篇文章《IIS日志存入数据库之一:ODBC》中,我提到了ODBC方式保存的缺点,即:无法保存响应时间以及接收和响应的字节数。

如果一定要获取响应时间以及接收和响应的字节数的话,就要另想办法了。备选的方法有:

(1)寻找有没有现成的IIS日志模块。

(2)重写IIS的日志模块。

(3)在现有的IIS日志模块的基础上进行改造。

下面是对三种备选方法的探索:

(1)针对方法1,在IIS的官网上找到了一个名为Adanced logging的日志模块,,,然并卵。

(2)针对方法2,改写的工作量较大,且可以会性能问题,故抛弃。

(3)针对方法3,发现iis日志的保存目标可以为ETW事件,故采用。如下图所示:

下面介绍一下如何订阅IIS的ETW事件。ps:关于ETW事件的介绍,请查看我的另外一篇文章:《在.net中使用ETW事件的方法

核心代码

         private void button1_Click(object sender, EventArgs e)
{
try
{ using (var session = new TraceEventSession("IIS-Logging")) // 创建一个session
{
session.EnableProvider("Microsoft-Windows-IIS-Logging"); // Microsoft-Windows-IIS-Logging 是IIS日志模块提供的provider的名称 session.Source.Registered.All += Registered_All; // 注册事件处理函数 session.Source.Process(); // Wait for incoming events (forever).
}
}
catch
{
}
} /// <summary>
/// 事件处理函数
/// </summary>
/// <param name="data"></param>
void Registered_All(TraceEvent data)
{
try
{
string logString = data.FormattedMessage; // 返回日志项的字符串形式 // 将文本转换成对象
IISLogEntry logEntry = new IISLogEntry(logString); IISLogEntry.Add(logEntry);
}
catch
{
}
}

 上述代码创建会话了,绑定了事件源(IIS的ETW事件提供者),订阅了事件源。这段代码有两个关键点:

  (1)怎么知道IIS日志模块的事件提供程序的名称是“Microsoft-Windows-IIS-Logging”呢?答案是通过命令行指令:logman query providers。下面是这个指令返回的结果:

  (2)在绑定ETW事件处理程序的时候,我们使用了session.Source.Registered, session.Source.Registered返回了一个RegisteredTraceEventParser对象,通过这个对象我们才能在事件处理程序中使用data.FormattedMessage来取得日志项的内容。有关RegisteredTraceEventParser,官方文档中有这么一句:

  RegisteredTraceEventParser – which knows about any event provider that registers itself with the operating system (using the wevtutil command)).

  This includes most providers that ship with the windows operating system that are NOT the kernel provider or EventSources.  You can see a list of such providers with the ‘logman query providers’ command.

外围代码(解析日志字符串,存入数据库)

 using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; [Table("IISLogEntry")]
public class IISLogEntry
{
[Key]
public long Id { get; set; } /// <summary>
/// 服务端信息
/// </summary>
public Server Server { set; get; } /// <summary>
/// 客户端信息
/// </summary>
public Client Client { get; set; } /// <summary>
/// 请求信息
/// </summary>
public Request Request { get; set; } /// <summary>
/// 响应信息
/// </summary>
public Response Response { get; set; } /// <summary>
/// 构造函数
/// </summary>
public IISLogEntry() { } /// <summary>
/// 构造函数。使用字符串来构造一个日志对象
/// </summary>
/// <param name="logString"></param>
public IISLogEntry(string logString)
{
try
{
string[] array = logString.Trim().Split(new char[] { ' ' }); Dictionary<string, string> dictionary = new Dictionary<string, string>();
//将数组中的值放入到字典中,奇数项为key,偶数项为value
for (int i = ; i < array.Length; i++)
{
if (i % == )
{
dictionary.Add(array[i], "");
}
if (i % == )
{
dictionary[array[i - ]] = array[i];
}
} this.Server = new Server()
{
IP = dictionary["s-ip"],
Port = int.Parse(dictionary["s-port"]),
Name = dictionary["s-computername"]
};
this.Client = new Client()
{
IP = dictionary["c-ip"],
UserAgent = dictionary["cs(User-Agent)"],
UserName = dictionary["cs-username"]
};
this.Request = new Request()
{
RequestDateTime = DateTime.Now,
Method = dictionary["cs-method"],
UriResource = dictionary["cs-uri-stem"],
UriQuery = dictionary["cs-uri-query"],
BytesReceived = this.ConvertToLong(dictionary["cs-bytes"])
};
this.Response = new Response()
{
TimeTaken = this.ConvertToLong(dictionary["time-taken"]),
BytesSent = this.ConvertToLong(dictionary["sc-bytes"]),
Status = int.Parse(dictionary["sc-status"]),
SubStatus = int.Parse(dictionary["sc-substatus"]),
Win32Status = int.Parse(dictionary["sc-win32-status"]),
};
}
catch (Exception exp)
{
throw new Exception("格式转换失败。\n日志字符串为:" + logString + "\n异常信息:" + exp.Message);
}
} //**************************** CRUD ************************************
public static bool Add(IISLogEntry data)
{
using (IISLogDbContext db = new IISLogDbContext())
{
db.IISLogEntries.Add(data); try
{
db.SaveChanges();
return true;
}
catch
{
return false;
}
}
} /// <summary>
/// 将带千分号的字符串转换成长整型。如:4,939
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
private long ConvertToLong(string str)
{
string str2 = str.Replace(",", "");
return long.Parse(str2);
} } /// <summary>
/// 服务端信息
/// </summary>
[ComplexType]
public class Server
{
/// <summary>
/// 服务器名称。对应:s-computername
/// </summary>
[MaxLength()]
public string Name { set; get; } /// <summary>
/// 服务器IP。对应:s-ip
/// </summary>
[MaxLength()]
public string IP { set; get; } /// <summary>
/// 服务器端口。对应:s-port
/// </summary>
public int Port { set; get; } } /// <summary>
/// 客户端信息
/// </summary>
[ComplexType]
public class Client
{
/// <summary>
/// 客户端IP。对应:c-ip
/// </summary>
[MaxLength()]
public string IP { set; get; } /// <summary>
/// 客户端所使用的用户代理。对应: cs(User-Agent)
/// </summary>
[MaxLength()]
public string UserAgent { set; get; } /// <summary>
/// 登录的用户名。对应: cs-username
/// </summary>
[MaxLength()]
public string UserName { set; get; } } /// <summary>
/// 请求信息
/// </summary>
[ComplexType]
public class Request
{
/// <summary>
/// 请求时间。对应:date和time
/// </summary>
public DateTime RequestDateTime { set; get; } /// <summary>
/// 方法。对应:cs-method
/// </summary>
[MaxLength()]
public string Method { set; get; } /// <summary>
/// uri资源。对应:cs-uri-stem
/// </summary>
[MaxLength()]
public string UriResource { get; set; } /// <summary>
/// uri查询。对应:cs-uri-query
/// </summary>
[MaxLength()]
public string UriQuery { get; set; } /// <summary>
/// 接收的字节数,单位为byte。对应:cs-bytes
/// </summary>
public long BytesReceived { get; set; } } /// <summary>
/// 响应信息
/// </summary>
[ComplexType]
public class Response
{
/// <summary>
/// 状态码。对应:sc-status
/// </summary>
public int Status { get; set; } /// <summary>
/// 子状态码。 对应:sc-substatus
/// </summary>
public int SubStatus { get; set; } /// <summary>
/// win32状态码。 对应:sc-win32-status
/// </summary>
public int Win32Status { get; set; } /// <summary>
/// 发送的字节数,单位为byte。对应:sc-bytes
/// </summary>
public long BytesSent { get; set; } /// <summary>
/// 所用时间,单位为ms。对应: time-taken
/// </summary>
public long TimeTaken { get; set; } }

上述代码是日志项实体。这里我们使用了EF作为ORM框架,mssql作为数据库。

using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions; public class IISLogDbContext : DbContext
{ public IISLogDbContext()
: base("IISLog")
{
} /// <summary>
/// 这个类的注释中的值,只用于在开发的时候进行选配
/// </summary>
static IISLogDbContext()
{
// Database.SetInitializer(new DropCreateDatabaseAlways<IISLogDbContext>());
// Database.SetInitializer(new CreateDatabaseIfNotExists<IISLogDbContext>());
// Database.SetInitializer(new DropCreateDatabaseIfModelChanges<IISLogDbContext>());
} /// <summary>
/// 初始化,当模型创建的时候,
/// </summary>
/// <param name="modelBuilder"></param>
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); // 移除协定:表名复数化
base.OnModelCreating(modelBuilder);
} // ********************* DbSet ************************** public DbSet<IISLogEntry> IISLogEntries { get; set; } }

上述代码是数据上下文。

最后提一句,怎么找到官方文档呢?在使用nuget获取Microsoft TraceEvent Library之后,工程文件中就会多一个名为“_TraceEventProgrammersGuide.docx”的文件,它就是官方文档。如下所示:

IIS日志存入数据库之二:ETW的更多相关文章

  1. IIS日志存入数据库之一:ODBC

    园内@Fish Li的文章<IIS日志-网站运维的好帮手>中介绍将IIS的文本格式的文件导入数据库的方法.在实践中,我们发现导数据的速度很慢,一个200M的日志文件居然要近100分钟.我们 ...

  2. WebAPI + log4net日志 存入数据库

    1.首先选择你的项目 打开net管理控制台 输入 install-package log4net 进行安装  也可以 在net包 搜索 log4net 2.安装完之后 在Models文件夹 创建一个L ...

  3. IIS日志-网站运维的好帮手

    对于一个需要长期维护的网站来说,如何让网站长久稳定运行是件很有意义的事情. 有些在开发阶段没有暴露的问题很有可能就在运维阶段出现了,这也是很正常的. 还有些时候,我们希望不断地优化网站,让网站更快速的 ...

  4. 网站运维工具使用iis日志分析工具分析iis日志(iis日志的配置)

    我们只能通过各种系统日志来分析网站的运行状况,对于部署在IIS上的网站来说,IIS日志提供了最有价值的信息,我们可以通过它来分析网站的响应情况,来判断网站是否有性能问题,或者存在哪些需要改进的地方 对 ...

  5. 【转】IIS日志-网站运维的好帮手

    对于一个需要长期维护的网站来说,如何让网站长久稳定运行是件很有意义的事情. 有些在开发阶段没有暴露的问题很有可能就在运维阶段出现了,这也是很正常的. 还有些时候,我们希望不断地优化网站,让网站更快速的 ...

  6. 利用LogParser将IIS日志插入到数据库

    利用LogParser将IIS日志插入到数据库 上面的博文是定制一个计划任务来将log日志定时的导入数据库      下面这篇博文是用cmd指令将日志导入到一张sql表中,是一次性操作   Log P ...

  7. 把IIS日志导入到数据库

    1.建表 CREATE TABLE [dbo].[inetlog0828]( [date] [date] NULL, ) NULL, ) NULL, ) NULL, ) NULL, ) NULL, [ ...

  8. MySQL使用二进制日志恢复数据库

    一.二进制日志简介 MySQL有不同类型的日志,其中二进制文件记录了所有对数据库的修改,如果数据库因为操作不当或其他原因丢失了数据,可以通过二进制文件恢复. 在my.ini文件中设置了log-bin, ...

  9. log4net 添加自定义日志到数据库

    添加操作日志到数据库举例: (一)建立数据库的操作日志表,如下我建立了一个简单的日志表 (二)配置文件中的配置如下 <log4net> <!--错误日志记录数据库--> < ...

随机推荐

  1. vim窗口切换

    参考资料: http://www.cnblogs.com/litifeng/p/8282479.html 当用vim写代码的时候,我喜欢一边看着头文件中结构的定义,一边编写实现的代码,这样就经常用到多 ...

  2. jQuery动画函数回调

    $("#show").click(function () { //function 是显示完成之后的回调函数 $("p").show(2000,function ...

  3. Centos 装系统 配置网卡,校准时间

    Vclient -控制台: 1.编辑网卡,第一块为外网,第二块为内网 #vi /etc/sysconfig/network-scripts/ifcfg-ens160 TYPE=Ethernet NAM ...

  4. rap2与postman自动化测试

    rap2的接口数据可以全部导入postman: 在collections集合里面点击run;

  5. visual studio 修改注释快捷键,和断点

    修改成alt+3和alt+4.效果不错 修改插入断点快捷键.这样按F12 就可以插入删除断点了.很爽 tab是批量加缩进 shift+tab 是批量减缩进

  6. 调用webservice时,产生android.os.NetworkOnMainThreadException错误

    android.os.NetworkOnMainThreadException 网上搜索后知道是因为版本问题,在4.0之后在主线程里面执行Http请求都会报这个错,也许是怕Http请求时间太长造成程序 ...

  7. 64位ubuntu 兼容32位

    http://www.cnblogs.com/mliudong/p/4086797.html 首先要打开64位系统对32位的支持 第一步:确认64为架构的内核 dpkg --print-archite ...

  8. linux_配置三台虚拟机免密登录

    在node01上面直接生成公钥和私钥 ssh-keygen --> 四下回车 ll -a 进行查看,发现出现.ssh文件即已经生成 将此node01的公钥拷贝到第二台机器上 ssh-copy-i ...

  9. render函数的简单使用

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  10. 2019.02.09 codeforces451 E. Devu and Flowers(容斥原理)

    传送门 题意简述:给出n堆花,对于第j堆,有f[j]朵花,每堆花的颜色不同,现在要从中选出s朵,求方案数. 思路: 假设所有花没有上限直接插板法,现在有了上限我们用容斥扣掉多算的 状压一下再容斥:fi ...