zoukankan      html  css  js  c++  java
  • 使用LogParser分析日志

    系统运维,少不了分析系统日志,微软有个工具Log Parser可以帮助你分析日志。它功能强大,使用简单,可以分析基于文本的日志文件、XML 文件、CSV(逗号分隔符)文件,以及操作系统的事件日志、注册表、文件系统、Active Directory。它可以像使用 SQL 语句一样查询分析这些数据,甚至可以把分析结果以各种图表的形式展现出来。

    Log Parser可以到微软的网站下载,安装完后,就会有命令行的执行程序LogParser.exe,供API使用的LogParser.dll及说明文件LogParser.chm,里面还会有一些Sample Code可以供参考.

    Log Parser支持的格式很多,输入格式如下:

    logparseinput

    输出格式如下:

    logparseoutput

    通过 .NET Framework 的 COM interop (COM 交互操作)特性,可以很方便地在 .NET 应用程序中使用 Log Parser,.NET Framework 的 COM interop 是通过 Runtime Callable Wrappers (RCW) 来实现对 COM 的操作的,RCW 是 .NET 中的一个类。

    现在要玩的是,怎么用LogParser.dll来开发更适合的API,其实命令行的做法就可以满足大部份的需求,但有时有时特殊的判断,在命令行模式下就有难度了,比如说,我们需要用程序自动去处理大批量的日志文件分析等,所以这时用API就方便很多.

    下面我们用.NET封装下LogParser的Com接口,从LogParser的操作流程来看,无非就是不同格式文件的日志文件的输入,通过类SQL的分析输出我们需要的结果,核心算法就是类似于

              // 初始化LogQuery 对象 
                var logQuery = new LogQueryClass();

                // 缓存输入上下文 
                if (myInputContext == null) 
                    myInputContext = GetInputContext(); 
                // 执行查询   
                var oRecordSet = logQuery.Execute(query, myInputContext); 
               
                 // 浏览记录 
                for (; !oRecordSet.atEnd(); oRecordSet.moveNext()) 
                { 
                    // 获取当前的记录 
                    ILogRecord logRecord = oRecordSet.getRecord();              
                } 
                oRecordSet.close();

    使用OOP方式封装接口,大家很容易的就会得出类似下面的设计,类图如下:

    ClassDiagram1

    每一种类型的日志的分析主要是格式的不同,通过一个配置类去记录每种类型的不同配置,根据配置去生成相应的输入、输出格式类。下面我们就来做个简单的Demo来演示下IIS日志分析。

          private void QryData()
    {

    //构造查询语句
    StringBuilder query = new StringBuilder("SELECT ");
    if (txtRecord.Text.Trim().Length > 0)
    {
    int rec = 0;
    if (int.TryParse(txtRecord.Text.Trim(), out rec))
    {
    if (rec > 0)
    {//如果有值,而且数字大于0,则取数字取得Top n
    query.Append(" TOP ");
    query.Append(rec);
    }
    }
    }
    if (btnLocalTime.Checked)
    {
    query.Append("UserIP,Username,TO_LOCALTIME(TO_TIMESTAMP(date,time)) as LocalTime");
    }
    else
    {
    query.Append("UserIP,Username,TO_TIMESTAMP(date,time) as LocalTime");
    }

    query.Append(",ServiceInstance,HostName,ServerIP,TimeTaken,BytesSent,bytesReceived,StatusCode" +
    ",Win32StatusCode,RequestType,Target,Parameters " +
    "FROM ");
    query.Append(openFileDialog1.FileName);
    if (txtWhere.Text.Trim().Length > 0)
    {
    query.Append(" Where ");
    query.Append(txtWhere.Text.Trim());
    }

    IISLogParserConfig config = new IISLogParserConfig()
    {
    Codepage =936,

    };
    IISLogParser logParser = new IISLogParser(config);
    DataTable dt = logParser.RunQuery(query.ToString());
    dataGridView1.DataSource = dt;
    }

    这段Code就是把Log读进来,并用DataGrid去显示,如果IIS Log里的时间不是当地时间,还可用TO_LOCALTIME做时间转换,看这Script,还可以用 Select Top n from 文件路径,这里有一点要注意,路径不能有中文名称,Query有那些可以用,可以参考说明文件的Query Syntax章节.

    LogParserQuerySyntax

    通过命令生成dll:C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\TlbImp.exe C:\Program Files\Log Parser 2.2\LogParser.dll /out:E:\Interop.MSUtil.dll

    直接下载 Interop.MSUtil.dll

    LogParser功能非常的强大,可以分析IIS日志,系统事件日志,CSV,XML等格式日志,同时也支持接口编程——可用C#调用LogParser的Com,其效率非常之高,特别是对大的日志文件(几十M几百M甚至几G的文本日志文件),使用LogParser能够快速查出你所要的数据,一些常用的方法在LogParser的帮助文档中都有介绍,下面简单举两例子。

    1.分析IIS日志
    --统计iis日志404错误的页面
    Logparser.exe "SELECT distinct count(*) as Times,cs-host as Host,cs-uri-stem as PageUrl into 'D:\Loginfo\IISLog\error\20100428.log.txt' FROM D:\Loginfo\IISLog\100428.log where sc-status=404 Group by Host,PageUrl order by Times desc" -o:tsv

    2.分析自定义的日志
    通常用程序定义的日志都设为tsv格式的,即列与列之间用 \t 隔开,或者用特殊的组合符号避免日志中本身就包含分隔符,如“\t|\t”,有利于后续的分析。
    与系统日志相比,自定义的日志需要声明一个头文件,列名格式与日志内容的列一致,就像SQL表中的列名样。
    LogParser.exe "select distinct Host, Uid,SessionId, CASE STRLEN(TRIM(AccountId)) WHEN NULL THEN 0 ELSE 1 END as IsLogin into C:\****.txt from 'D:\Loginfo\LogInfo_1004280930.log' where Host >''" -i:TSV -fixedSep:ON -iHeaderFile D:\Loginfo\PVLogHeadFile\PVLogHeadFile.txt -o:tsv -fileMode:0
    -fileMode:0 为0表示追加,为1表示覆盖原文件。

    3.用LogParser导数据到数据库中
    (1)导入到远程数据库存在的表中
    LogParser.exe "SELECT * FROM 'D:\LogInfo\LogInfo_1001032230.log' TO dbo.[table]" -o:SQL -server:[服务器IP] -driver:"SQL Server" -database:[数据库] -username:[用户名] -password:[密码] -i:tsv
    (2)导入到本机数据库存在的表中
    LogParser.exe "select * into [数据库].dbo.[table] from 'D:\LogInfo\LogInfo_1001032230.log'" -o:sql -database:[数据库]  -i:tsv -iCodepage:65001
    (3)导入并创建表
    LogParser.exe "SELECT * FROM 'D:\LogInfo\LogInfo_1001032230.log' TO dbo.[table]" -o:SQL -server:[服务器IP] -driver:"SQL Server" -database:[数据库] -username:[用户名] -password:[密码] -i:tsv -createtable:ON
    说明:
    (1)对于已存在的表,需要在前面预留两个字段,LogParser导入的时候,会把文件名和行号也导入到表中
    (2)字段类型,对于已存在的表,里面设置的字段类型必须与要导入的日志文件的类型一一匹配,否则会导入失败,对于创建表的情况,如果需要导入的字段中包含长整型,则导入后的数据也是不对的,创建的表为int型,因此建议是手动建表。

    4.C#调用LogParser Com
    场景:某网站有一模块,被调用成功,失败都会记一笔日志到文本文件中,需要实时监控失败率。
    说明:日志是以一定的格式记录的,第一列表示时间,第二列为描述,包含调用参数,调用是否成功等信息
    添加引用:
    using LogQuery = MSUtil.LogQueryClassClass;
    using LogRecordSet = MSUtil.ILogRecordset;
    using TsvInputFormat = MSUtil.COMTSVInputContextClassClass;

       1: /// <summary>
       2: /// 计算失败率
       3: /// </summary>
       4: public double GetFailureRate(string headerFile, string logPath)
       5: {
       6: double failureRate = 1;
       7: LogQuery oLogQuery = new LogQuery();
       8: TsvInputFormat oTsvInputFormat = new TsvInputFormat();
       9: oTsvInputFormat.iHeaderFile = headerFile;
      10: LogRecordSet oRecordSet;
      11: double totalQty = 0;
      12: double successQty = 0;
      13: string query = string.Empty;
      14: try
      15: {
      16: #region 所有的条数--Log中包含“reccode=”的行数
      17: query = @"select count(*) as qty from '" + logPath + "' where loginfo like '%reccode=%'";
      18: oRecordSet = oLogQuery.Execute(query, oTsvInputFormat);
      19: if (!oRecordSet.atEnd())
      20: {
      21: totalQty = (int)oRecordSet.getRecord().getValue("qty");
      22: }
      23: oRecordSet.close();
      24: #endregion
      25: 
      26: #region 成功的条数--Log中包含“reccode=0,”的行数
      27: query = @"select count(*) as qty from '" + logPath) + "' where loginfo like '%reccode=0,%'";
      28: oRecordSet = oLogQuery.Execute(query, oTsvInputFormat);
      29: if (!oRecordSet.atEnd())
      30: {
      31: successQty = (int)oRecordSet.getRecord().getValue("qty");
      32: }
      33: oRecordSet.close();
      34: #endregion
      35: 
      36: if (totalQty > 0 && totalQty >= successQty)
      37: {
      38: failureRate = Math.Round((1 - successQty / totalQty) * 100, 2);
      39: }
      40: }
      41: catch (Exception ex)
      42: {
      43: ReqLog.WriteLine(ex.ToString());
      44: }
      45: 
      46: return failureRate;
      47: }
      48: 
      49: /// <summary>
      50: /// 计算调用平均时间
      51: /// </summary>
      52: public double GetAvgLockSec(string headerFile, string logPath)
      53: {
      54: double avgLockSec = 0;
      55: LogQuery oLogQuery = new LogQuery();
      56: TsvInputFormat oTsvInputFormat = new TsvInputFormat();
      57: oTsvInputFormat.iHeaderFile = headerFile;
      58: try
      59: {
      60: string query = "select AVG(TO_REAL(EXTRACT_VALUE(EXTRACT_TOKEN(SUBSTR(loginfo,INDEX_OF(loginfo,'locksec=')),0,' '),'locksec'))) as avglocksec from '" + logPath + "' where loginfo like '%locksec=%'";
      61: LogRecordSet oRecordSet = oRecordSet = oLogQuery.Execute(query, oTsvInputFormat);
      62: if (!oRecordSet.atEnd())
      63: {
      64: double item = 0;
      65: if (!oRecordSet.getRecord().isNull(0))
      66: {
      67: item = double.Parse(oRecordSet.getRecord().getValue(0).ToString());
      68: }
      69: avgLockSec = Math.Round(item, 2);
      70: }
      71: oRecordSet.close();
      72: }
      73: catch (Exception ex)
      74: {
      75: ReqLog.WriteLine(ex.ToString());
      76: }
      77: return avgLockSec;
      78: }
  • 相关阅读:
    Android开发总结
    LeakCanary原理分析
    机器学习
    Kivy 中文教程 实例入门 简易画板 (Simple Paint App):2. 实现绘图功能
    Python 零基础 快速入门 趣味教程 (咪博士 海龟绘图 turtle) 3. 循环
    Kivy 中文教程 实例入门 简易画板 (Simple Paint App):1. 自定义窗口部件 (widget)
    Python 零基础 快速入门 趣味教程 (咪博士 海龟绘图 turtle) 2. 变量
    Python 零基础 快速入门 趣味教程 (咪博士 海龟绘图 turtle) 1. 神秘朋友
    Python 零基础 快速入门 趣味教程 (咪博士 海龟绘图 turtle) 0. 准备工作
    远程显示(操作) 服务器 GUI 程序(图形化界面) (基于 X11 Forwarding + Centos + MobaXterm)
  • 原文地址:https://www.cnblogs.com/xx_cs/p/2433281.html
Copyright © 2011-2022 走看看