• 使用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: }
  • 相关阅读:
    C#之获取本地IP地址
    C#中对Excel进行操作
    C#中的TCP通讯与UDP通讯
    Flex 学习
    正则表达式实例
    sass调试--页面看到sass文件而不是css文件问题
    webpack+vue-loader 在单独.vue组件中使用sass-loader编译sass报错问题not a valid Win32 applictation
    SVG图案填充-Pattern
    jQuery小技巧
    代码整洁一
  • 原文地址:https://www.cnblogs.com/xx_cs/p/2433281.html
Copyright © 2020-2023  润新知