IIS日志存入数据库之二:ETW
在上一篇文章《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的更多相关文章
- IIS日志存入数据库之一:ODBC
园内@Fish Li的文章<IIS日志-网站运维的好帮手>中介绍将IIS的文本格式的文件导入数据库的方法.在实践中,我们发现导数据的速度很慢,一个200M的日志文件居然要近100分钟.我们 ...
- WebAPI + log4net日志 存入数据库
1.首先选择你的项目 打开net管理控制台 输入 install-package log4net 进行安装 也可以 在net包 搜索 log4net 2.安装完之后 在Models文件夹 创建一个L ...
- IIS日志-网站运维的好帮手
对于一个需要长期维护的网站来说,如何让网站长久稳定运行是件很有意义的事情. 有些在开发阶段没有暴露的问题很有可能就在运维阶段出现了,这也是很正常的. 还有些时候,我们希望不断地优化网站,让网站更快速的 ...
- 网站运维工具使用iis日志分析工具分析iis日志(iis日志的配置)
我们只能通过各种系统日志来分析网站的运行状况,对于部署在IIS上的网站来说,IIS日志提供了最有价值的信息,我们可以通过它来分析网站的响应情况,来判断网站是否有性能问题,或者存在哪些需要改进的地方 对 ...
- 【转】IIS日志-网站运维的好帮手
对于一个需要长期维护的网站来说,如何让网站长久稳定运行是件很有意义的事情. 有些在开发阶段没有暴露的问题很有可能就在运维阶段出现了,这也是很正常的. 还有些时候,我们希望不断地优化网站,让网站更快速的 ...
- 利用LogParser将IIS日志插入到数据库
利用LogParser将IIS日志插入到数据库 上面的博文是定制一个计划任务来将log日志定时的导入数据库 下面这篇博文是用cmd指令将日志导入到一张sql表中,是一次性操作 Log P ...
- 把IIS日志导入到数据库
1.建表 CREATE TABLE [dbo].[inetlog0828]( [date] [date] NULL, ) NULL, ) NULL, ) NULL, ) NULL, ) NULL, [ ...
- MySQL使用二进制日志恢复数据库
一.二进制日志简介 MySQL有不同类型的日志,其中二进制文件记录了所有对数据库的修改,如果数据库因为操作不当或其他原因丢失了数据,可以通过二进制文件恢复. 在my.ini文件中设置了log-bin, ...
- log4net 添加自定义日志到数据库
添加操作日志到数据库举例: (一)建立数据库的操作日志表,如下我建立了一个简单的日志表 (二)配置文件中的配置如下 <log4net> <!--错误日志记录数据库--> < ...
随机推荐
- snort学习笔记
Snort有三种工作模式:嗅探器.数据包记录器.网络入侵检测系统(ids). 嗅探器模式仅仅是从网络上读取数据包并作为连续不断的流显示在终端上. 数据包记录器模式把数据包记录到硬盘上. 网络入侵检测模 ...
- BUG(1):一个关于指针的bug
是时候记录一下这个让我栽了两次的bug了. 具体情况如下: #include <stdio.h>#include <stdlib.h> struct app_info_t { ...
- oralce的lag和lead函数
https://www.cnblogs.com/always-online/p/5010185.html
- linux- Fedora25 下 解决anacondas3 与ibus冲突问题
问题:当我们安装了anaconda3之后,会发现ibus-setup进不去. 原因: 是因为ibus-setup的python应该使用python2. 而当我们安装了anaconda3之后,调用pyt ...
- mysql bigint ,int , smallint,tinyint 的范围
bigint 8字节 64位 int 4字节 32位 smallint 2字节 16位 tinyint 1字节8位 .. 范围 -128 到 127 , 如果是无符号 ,则返回 位 0-255 ...
- js对象通过属性路径获取属性值 - getPropByPath
function getPropByPath(obj, path) { let tempObj = obj; path = path.replace(/\[(\w+)\]/g, '.$1'); pat ...
- requestAnimationFrame 完美兼容封装
完美兼容封装: (function() { var lastTime = 0; var vendors = ['webkit', 'moz']; for(var x = 0; x < vendo ...
- .net使用NPOI的XSSFWorkbook进行web开发中导出Excel
之前也使用过NPOI导出excel,这次是因为在导出的excel里新增了几个列,正好超出了255的限制,所以又要改了. 今天主要出了4个问题: 1. Invalid column index (256 ...
- linux文件系统问题:wrong fs type, bad option, bad superblock
http://blog.itpub.net/26006637/viewspace-1059946/ 报错内容: mount: wrong fs type, bad option, bad superb ...
- php-fpm 的 pm.start_servers 参数调整
大家注意一下 在 php-fpm 的配置文件中, pm.start_servers 必须是介于 pm.min_spare_servers 和 pm.max_spare_servers 这个值之间 ...