在上一篇文章《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. UI设计师需要熟记的45个快捷键Windows、Mac

    大家都知道PS快捷键很多,其实没必要都记住,今天为大家整理了45个比较实用的,别忘了收藏. 图层 填充图层 MAC: Alt+Backspace (前景) or Cmd+Backspace (背景) ...

  2. MySQL单行注释和多行释

    单行注释:# 或者 -- 多行注释:/* ... */ 使用 # 时,# 和注释的内容之间可以没有空格 使用 -- 时,-- 和注释内容之间必须有空格

  3. Nginx+Tomcat安装与配置(windows版)

    相信很多人都听过nginx,这个小巧的东西慢慢地在吞食apache和IIS的份额.那究竟它有什么作用呢?可能很多人未必了解. 说到反向代理,可能很多人都听说,但具体什么是反向代理,很多人估计就不清楚了 ...

  4. vb中去掉string数组的一部分

    今天碰到一个问题,登陆的时候,如果不需要验证手机号为空,则不去验证手机号 因为登陆的时候所有的验证信息都存放在一个数组里 Dim CheckUserInfo() As String ={UserBir ...

  5. SQL表两列取一列唯一值的记录

    问下SQL表两列取一列唯一值的 A列         B列       C列 1001      AA      2012-01-02 1001      BB      2012-02-05 100 ...

  6. 如何从jks文件中导出公私钥

    1.从JKS转换到PKCS12 #keytool -importkeystore -srckeystore <MY_KEYSTORE.jks> -destkeystore <MY_F ...

  7. 各种 on事件触发js代码

    [转]各种 on事件触发js代码 1.onmouseenter:当鼠标进入选区执行代码 <div style="background-color:red" onmouseen ...

  8. oracle undo表空间

    查询undo表空间状态 "Bytes(M)" FROM dba_undo_extents GROUP BY tablespace_name, status; Undo表空间的状态( ...

  9. Cisco interview

    A.  1. Self-introduction I am Yanlin He . I am a master degree candidate of school of infomation sci ...

  10. 解决mysql默认的8小时自动断开连接

    语言:javaEE 框架:spring mvc+spring+mybatis 数据库:mysql8 WEB服务器:tomcat8 背景: 在试运营阶段发现发生“连接超时”异常 抛出异常: Cause: ...