IIS日志存入数据库之二:ETW

时间:2022-08-31 13:23:45

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

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

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

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

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

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

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

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

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

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。下面是这个指令返回的结果:

IIS日志存入数据库之二:ETW

  (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日志存入数据库之二:ETW的更多相关文章

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

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

  2. WebAPI &plus; log4net日志 存入数据库

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

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

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

  4. 网站运维工具使用iis日志分析工具分析iis日志&lpar;iis日志的配置&rpar;

    我们只能通过各种系统日志来分析网站的运行状况,对于部署在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> <!--错误日志记录数据库--> &lt ...

随机推荐

  1. javascript动画系列第二篇——磁性吸附

    × 目录 [1]范围限定 [2]拖拽范围 [3]磁性吸附 前面的话 上一篇,我们介绍了元素拖拽的实现.但在实际应用中,常常需要为拖拽的元素限定范围.而通过限定范围,再增加一些辅助的措施,就可以实现磁性 ...

  2. 基于Tomcat的Solr3&period;5集群部署

    基于Tomcat的Solr3.5集群部署 一.准备工作 1.1 保证SOLR库文件版本相同 保证SOLR的lib文件版本,slf4j-log4j12-1.6.1.jar slf4j-jdk14-1.6 ...

  3. edittext把软键盘上的回车键改为搜索、发送或者 下一步,窗口随软键盘弹出而改变。

    http://m.blog.csdn.net/article/details?id=51350501 以上博文讲解很详细. 如图所示,有时候为了布局美观,在搜索时没有搜索按钮,而是调用软件盘上的按钮. ...

  4. android 通过WiFi进行adb调试

    第一种方式: 1.把Android SDK的下的 platform-tools 目录 加入到系统的path变量中 打开cmd命令行 输入adb 显示如下图则为设置成功 2.打开手机 下载一个叫WiFi ...

  5. hiho一下 第六十四周&Tab;Right-click Context Menu

    题目链接:hihocoder 第64周 题意概述: 上下文菜单是panel(面板)包括很多section(分区),一个分区里面至少包含一个菜单项.每一个菜单项都对应有一个子panel,这个panel可 ...

  6. oracle rownum paging issues

    rownum是oracle预处理字段,默认标序是1,只有记录集已经满足条件后才会进行后续编号.由于第一条记录rownum默认是1,而你的条件是rownum>=6 对第一条记录比较它的rownum ...

  7. 通过XmlSerializer 实现XML的序列化与反序列化

    通过XmlSerializer 我们可以十分简单的将Model与XML进行转换 官文在点这里 帮助类 using System; using System.Text; using System.Xml ...

  8. Mysql知识要点总结

    1.安装 要点:记得更改字符集 2.数据类型 常用数据类型:INT VARCHAR BLOG 3.操作数据库 SHOW DATABASES; CREATE DATABASE 名称; DROP DATA ...

  9. JNDI初认识

    JNDI即Java命名和目录接口,英文全称为Java Naming and Directory Interface,从字面上似乎十分晦涩,下面从理论和实际项目应用方面来阐述. 1.命名:在我们实际生活 ...

  10. Mysql启动时执行文件init-file的使用

    可以在配置文件里指定mysql启动以后初始执行的SQL文件, 其语法是: 在[mysqld]下指定: init-file="D:/mysql/test.sql",  后面为具体的s ...