tech.guitarrapc.cóm

Technical updates

PowerShell 6.0 のImport-Csv に W3C 拡張ログ ファイル形式のサポートを追加 #2482 について調べてみた

これを調べていたのは本を書いていたときなので、そろそろ一年経つのですがお蔵入りの前に出しておきます。 PowerShell 6.0 において、W3C 拡張ログが Import-Csv で読み込み可能になったという内容でリリースノートが出ているのですがその内容について。

docs.microsoft.com

目次

TL;DR

Import-Csv がW3Cログに対応したといっても、それはIISログではなくMicrosoft Exchange のログ (W3Cログ形式)となる。 なんという期待を裏切る罠。

まとめおいておきます。

Import-Csv in PowerShell 6.0 supports w3c log format, if delimiter is `,`. https://github.com/PowerShell/PowerShell/pull/2482 · GitHub

W3C 拡張ログとは

W3C に定義があります。

www.w3.org

Introduction で用途がだいたいわかるので引用します。

Most Web servers offer the option to store logfiles in either the common log format or a proprietary format. The common log file format is supported by the majority of analysis tools but the information about each server transaction is fixed. In many cases it is desirable to record more information. Sites sensitive to personal data issues may wish to omit the recording of certain data. In addition ambiguities arise in analyzing the common log file format since field separator characters may in some cases occur within fields. The extended log file format is designed to meet the following needs:

* Permit control over the data recorded.
* Support needs of proxies, clients and servers in a common format
* Provide robust handling of character escaping issues
* Allow exchange of demographic data.
* Allow summary data to be expressed.

重要な箇所はFormat にあります、引用します。

Format
An extended log file contains a sequence of lines containing ASCII characters terminated by either the sequence LF or CRLF. Log file generators should follow the line termination convention for the platform on which they are executed. Analyzers should accept either form. Each line may contain either a directive or an entry.

Entries consist of a sequence of fields relating to a single HTTP transaction. Fields are separated by whitespace, the use of tab characters for this purpose is encouraged. If a field is unused in a particular entry dash "-" marks the omitted field. Directives record information about the logging process itself.

Lines beginning with the # character contain directives. The following directives are defined:

Version: <integer>.<integer>
The version of the extended log file format used. This draft defines version 1.0.
Fields: [<specifier>...]
Specifies the fields recorded in the log.
Software: string
Identifies the software which generated the log.
Start-Date: <date> <time>
The date and time at which the log was started.
End-Date:<date> <time>
The date and time at which the log was finished.
Date:<date> <time>
The date and time at which the entry was added.
Remark: <text>
Comment information. Data recorded in this field should be ignored by analysis tools.
The directives Version and Fields are required and should precede all entries in the log. The Fields directive specifies the data recorded in the fields of each entry.

Example
The following is an example file in the extended log format:

#Version: 1.0
#Date: 12-Jan-1996 00:00:00
#Fields: time cs-method cs-uri
00:34:23 GET /foo/bar.html
12:21:16 GET /foo/bar.html
12:45:52 GET /foo/bar.html
12:57:34 GET /foo/bar.html

ここで最も重要なのは、Fields are separated by whitespace という箇所で、いわゆるデリミター(Delimiter/区切り文字) が 空白スペースであるという記述です。Exampleもそのようになっています。

Import-Csv でIIS ログを読んでみる

リリースノートを見ただけだと、W3Cログ = IIS ログが読み込めるのかと思った方もいらっしゃるかもしれません。 IISログを試していましょう。

#Software: Microsoft Internet Information Services 7.5
#Version: 1.0
#Date: 2013-06-24 10:56:45
#Fields: date time s-ip cs-method cs-uri-stem cs-uri-query s-port cs-username c-ip cs(User-Agent) sc-status sc-substatus sc-win32-status time-taken
2013-06-24 10:56:45 192.168.0.1 POST /xas/ - 80 - 222.222.222.222 Mozilla/5.0+(Windows+NT+6.1;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/27.0.1453.110+Safari/537.36 404 0 2 471

しかし次のようにデリミターは空白文字であり、Import-Csvでは区切りを認識できません。

PS> import-csv .\iis_log.log
 
 <#
 date time s-ip cs-method cs-uri-stem cs-uri-query s-port cs-username c-ip cs(User-Agent) sc-status sc-substatus sc-win32-status time-taken
------------------------------------------------------------------------------------------------------------------------------------------
2013-06-24 10:56:45 192.168.0.1 POST /xas/ - 80 - 222.222.222.222 Mozilla/5.0+(Windows+NT+6.1;+WOW64)+AppleWebKit/537.36+(KHTML
 #>

-Delimiter に " " を指定すると読み込みエラーが起こることから、ログのデリミターを変えるしかなさそうです。 IISログに関して、デリミターを変更できるか IIS_SCHEMA.xml で調整を試みるとできないことが分かります。

<!-- just for logFile section -->
<!-- there's no attribute, value to change delimiter -->
      <element name="logFile">
        <attribute name="logExtFileFlags" type="flags" defaultValue="Date, Time, ClientIP, UserName, ServerIP, Method, UriStem, UriQuery, TimeTaken, HttpStatus, Win32Status, ServerPort, UserAgent, HttpSubStatus, Referer">
          <flag name="Date" value="1"/>
          <flag name="Time" value="2"/>
          <flag name="ClientIP" value="4"/>
          <flag name="UserName" value="8"/>
          <flag name="SiteName" value="16"/>
          <flag name="ComputerName" value="32"/>
          <flag name="ServerIP" value="64"/>
          <flag name="Method" value="128"/>
          <flag name="UriStem" value="256"/>
          <flag name="UriQuery" value="512"/>
          <flag name="HttpStatus" value="1024"/>
          <flag name="Win32Status" value="2048"/>
          <flag name="BytesSent" value="4096"/>
          <flag name="BytesRecv" value="8192"/>
          <flag name="TimeTaken" value="16384"/>
          <flag name="ServerPort" value="32768"/>
          <flag name="UserAgent" value="65536"/>
          <flag name="Cookie" value="131072"/>
          <flag name="Referer" value="262144"/>
          <flag name="ProtocolVersion" value="524288"/>
          <flag name="Host" value="1048576"/>
          <flag name="HttpSubStatus" value="2097152"/>
        </attribute>
        <attribute name="customLogPluginClsid" type="string" defaultValue=""/>
        <attribute name="logFormat" type="enum" defaultValue="W3C">
          <enum name="IIS" value="0"/>
          <enum name="NCSA" value="1"/>
          <enum name="W3C" value="2"/>
          <enum name="Custom" value="3"/>
        </attribute>
        <attribute name="logTargetW3C" type="flags" defaultValue="File">
          <flag name="File" value="1"/>
          <flag name="ETW" value="2"/>
        </attribute>
        <attribute name="directory" type="string" expanded="true" defaultValue="%SystemDrive%\inetpub\logs\LogFiles" validationType="nonEmptyString" />
        <attribute name="period" type="enum" defaultValue="Daily">
          <enum name="MaxSize" value="0"/>
          <enum name="Daily" value="1"/>
          <enum name="Weekly" value="2"/>
          <enum name="Monthly" value="3"/>
          <enum name="Hourly" value="4"/>
        </attribute>
        <attribute name="truncateSize" type="int64" defaultValue="20971520" validationType="integerRange" validationParameter="1048576,4294967295" />
        <attribute name="localTimeRollover" type="bool" defaultValue="false"/>
        <attribute name="enabled" type="bool" defaultValue="true" />
        <attribute name="logSiteId" type="bool" defaultValue="true" />
        <attribute name="flushByEntryCountW3CLog" type="uint" defaultValue="0" />
        <attribute name="maxLogLineLength" type="uint" validationType="integerRange" validationParameter="2,65536" defaultValue="65536" />
        <element name="customFields">
          <attribute name="maxCustomFieldLength" type="uint" validationType="integerRange" validationParameter="2,65536" defaultValue="4096" />
          <collection addElement="add" clearElement="clear">
            <attribute name="logFieldName" type="string" required="true" isUniqueKey="true" validationType="nonEmptyString" />
            <attribute name="sourceName" type="string" required="true" validationType="nonEmptyString" />
            <attribute name="sourceType" type="enum" required="true" >
              <enum name="RequestHeader" value="0"/>
              <enum name="ResponseHeader" value="1"/>
              <enum name="ServerVariable" value="2"/>
            </attribute>
          </collection>
        </element>
      </element>
      <element name="traceFailedRequestsLogging">
        <attribute name="enabled" type="bool" defaultValue="false" />
        <attribute name="directory" type="string" expanded="true" defaultValue="%SystemDrive%\inetpub\logs\FailedReqLogFiles"/>
        <attribute name="maxLogFiles" type="uint" defaultValue="50" validationType="integerRange" validationParameter="1,10000"/>
        <attribute name="maxLogFileSizeKB" type="uint" defaultValue="1024" validationType="integerRange" validationParameter="0,1048576"/>
        <attribute name="customActionsEnabled" type="bool" defaultValue="false"/>
      </element>

web.config でも変更できないのは、IIS10 でも変わっていません。

stackoverflow.com

docs.mendix.com

Advanced Logging ならわんちゃん....?

forums.iis.net

Import-Csv でExchange ログを読んでみる

Import-Csv が対象にしているのは、Delimiter が カンマ , つまり、csv のフォーマットです。 W3C でCSV フォーマットのログ、パッとでませんでしたがあります、Microsoft Exchange です。

docs.microsoft.com

これは実際に、PowerShell チームが Import-Csv で W3C を読むときのテストデータからもわかります。

https://github.com/PowerShell/PowerShell/blob/master/test/powershell/Modules/Microsoft.PowerShell.Utility/assets/TestImportCsv_W3C_ELF.csv

#Software: Microsoft Exchange Server
#Version: 15.0.0.0
#Log-type: Transport Connectivity Log
#Date: 2016-09-16T23:30:07.338Z
#Fields: Column1,Column2,Column 3
data1,1,A
data2,2,B
data3,3,C
data4,4,D

これを読んでみると、ヘッダ部分の # が無視されてデータが読み込まれたのが分かります。

PS> import-csv .\iis_log.csv
 
 # https://github.com/iSazonov/PowerShell/blob/0818b6c921c1970dc294669134266f878352891a/test/powershell/Modules/Microsoft.PowerShell.Utility/assets/TestImportCsv_W3C_ELF.csv
 <#
 Column1 Column2 Column 3
------- ------- --------
data1   1       A
data2   2       B
data3   3       C
data4   4       D
 #>

まとめ

Import-Csv で IISログの W3C ログは読めない、Exchange は読める。なるほど、Import-Csv ですからね。