Skip to content

Commit

Permalink
Merge a5faf0a into fca3b39
Browse files Browse the repository at this point in the history
  • Loading branch information
splatteredbits authored Feb 18, 2023
2 parents fca3b39 + a5faf0a commit 9715218
Show file tree
Hide file tree
Showing 12 changed files with 753 additions and 69 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

# 1.0.0

* Created `Import-W3CLog` function for parsing and importing W3C log files.
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

# Overview

The "W3CLogs" module...
The "W3CLogs" module has a single function `Import-W3CLog`, which parses and imports W3C log files.

# System Requirements

Expand All @@ -23,4 +24,16 @@ Save-Module -Name 'W3CLogs' -Path '.'
Import-Module -Name '.\W3CLogs'
```

# Commands
# Usage

Pass the path to a single log file to parse to the function's `Path` parameter:

```powershell
Import-W3CLog -Path 'log.log'
```

To parse multiple logs, pipe them in:

```powershell
Get-ChildItem -Recurse -Filter '*.log' | Import-W3CLog
```
422 changes: 422 additions & 0 deletions Tests/Import-W3CLog.Tests.ps1

Large diffs are not rendered by default.

46 changes: 0 additions & 46 deletions Tests/Import-W3CLogs.Tests.ps1

This file was deleted.

2 changes: 1 addition & 1 deletion Tests/Initialize-Test.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ try
{
$modules = [ordered]@{
'W3CLogs' = '..\W3CLogs';
'MODULE_NAMETestHelper' = 'MODULE_NAMETestHelper';
'W3CLogsTestHelper' = 'W3CLogsTestHelper';
}
foreach( $moduleName in $modules.Keys )
{
Expand Down
2 changes: 1 addition & 1 deletion Tests/W3CLogs.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ BeforeAll {
Describe 'W3CLogs' {
It 'should have about help topic' {
GivenModuleImported
ThenHelpTopic 'about_MODULE_NAME' -Exists
ThenHelpTopic 'about_W3CLogs' -Exists
}

It 'should only use approved verbs' {
Expand Down
155 changes: 155 additions & 0 deletions W3CLogs/Functions/Import-W3CLog.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@

function Import-W3CLog
{
<#
.SYNOPSIS
Parses and imports W3C log files.
.DESCRIPTION
The `Import-W3CLog` function parses and imports W3C log files, returning objects representing each line. Pass the
path to the log file to parse to the `Path` parameter, or to parse multiple files, pipe them into the function.
.EXAMPLE
Import-W3CLog -Path log.log
Demonstrates how to parse and import a single W3C log file by passing its path to the `Path` parameter.
.EXAMPLE
Get-ChildItem -Path C:\Inetpub\logs -Filter '*.log' -Recurse | Import-W3CLog
Demonstrates how to parse multiple logs by piping their paths to the `Import-W3CLog` function.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
[Alias('FullName')]
[String] $Path
)

process
{
Set-StrictMode -Version 'Latest'
Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

$Path = $Path | Resolve-Path
if (-not $Path)
{
return
}

$displayPath = $Path | Resolve-Path -Relative
if ($displayPath.StartsWith('..'))
{
$displayPath = $Path
}

Write-Verbose $displayPath

$fields = @()

$lineNum = 0
foreach ($line in (Get-Content -Path $Path))
{
$lineNum += 1

if (-not $line)
{
continue
}

if ($line.StartsWith('#'))
{
if ($line.StartsWith('#Fields: '))
{
$fields = $line.Split(' ') | Select-Object -Skip 1
}
else
{
Write-Verbose " $($line.Substring(1))"
}
continue
}

$errMsgPrefix = "$($displayPath) line $($lineNum): "

$entry = [W3CLogs.LogEntry]::New()

[String[]]$values = $line.Split(' ')
for ($idx = 0; $idx -lt $values.Length; ++$idx)
{
$propertyName = $fieldName = $fields[$idx]
if ($script:fieldPropertyMap.ContainsKey($fieldName))
{
$propertyName = $script:fieldPropertyMap[$fieldName]
}
else
{
$entry | Add-Member -Name $fieldName -MemberType NoteProperty
}

$value = $values[$idx]
if ($value -eq '-')
{
continue
}

if ($script:httpMethods.Contains($fieldName))
{
$value = [Net.Http.HttpMethod]::New($value)
}
elseif ($script:milliseconds.Contains($fieldName))
{
$value = [TimeSpan]::New(0, 0, 0, 0, $value)
}

try
{
$entry.$propertyName = $value
}
catch
{
$msg = "$($errMsgPrefix)Failed to convert $($fieldName) value ""$($value)"": $($_)"
Write-Error $msg -ErrorAction $ErrorActionPreference
}
}

$entry.DateTime = $entry.Date + $entry.Time

$hostname = 'example.com'
if ($entry.Host)
{
$hostname = $entry.Host
}
elseif ($entry.ServerIP -and $entry.ServerIP.AddressFamily -eq [Net.Sockets.AddressFamily]::InterNetwork)
{
$hostname = $entry.ServerIP.IPAddressToString
}

$queryString = ''
if ($entry.Query)
{
$queryString = "?$($entry.Query)"
}

$stem = $entry.Stem
if (-not $stem.StartsWith('/'))
{
$stem = "/$($stem)"
}

$url = "http://$($hostname)$($stem)$($queryString)"
try
{
$entry.Url = [Uri]::New($url)
}
catch
{
$msg = "$($errMsgPrefix)Failed to convert ""$($url)"" to a [Uri] object: $($_)"
Write-Error $msg -ErrorAction $ErrorActionPreference
}

$entry | Write-Output
}

}
}
12 changes: 6 additions & 6 deletions W3CLogs/W3CLogs.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@
RootModule = 'W3CLogs.psm1'

# Version number of this module.
ModuleVersion = '0.0.0'
ModuleVersion = '1.0.0'

# ID used to uniquely identify this module
GUID = ''
GUID = '278b8955-4db6-4794-b880-787f3c017a77'

# Author of this module
Author = 'WebMD Health Services'
Expand All @@ -36,7 +36,7 @@
Copyright = '(c) WebMD Health Services.'

# Description of the functionality provided by this module
Description = ''
Description = 'PowerShell module for working with W3C log files.'

# Minimum version of the Windows PowerShell engine required by this module
PowerShellVersion = '5.1'
Expand Down Expand Up @@ -76,6 +76,7 @@

# Functions to export from this module. Only list public function here.
FunctionsToExport = @(
'Import-W3CLog'
)

# Cmdlets to export from this module. By default, you get a script module, so there are no cmdlets.
Expand All @@ -102,7 +103,7 @@
PSData = @{

# Tags applied to this module. These help with module discovery in online galleries.
Tags = @( 'Desktop', 'Core' )
Tags = @( 'w3c', 'log', 'logs', 'parse', 'iis', 'http', 'import', 'Desktop', 'Core' )

# A URL to the license for this module.
LicenseUri = 'http://www.apache.org/licenses/LICENSE-2.0'
Expand All @@ -116,8 +117,7 @@
Prerelease = ''

# ReleaseNotes of this module
ReleaseNotes = @'
'@
ReleaseNotes = 'https://github.com/webmd-health-services/W3CLogs/blob/main/CHANGELOG.md'
} # End of PSData hashtable

} # End of PrivateData hashtable
Expand Down
38 changes: 37 additions & 1 deletion W3CLogs/W3CLogs.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,43 @@ Set-StrictMode -Version 'Latest'
# Functions should use $moduleRoot as the relative root from which to find
# things. A published module has its function appended to this file, while a
# module in development has its functions in the Functions directory.
$moduleRoot = $PSScriptRoot
$script:moduleRoot = $PSScriptRoot

$script:fieldPropertyMap = @{
'date' = 'Date';
'time' = 'Time';
's-ip' = 'ServerIP';
'cs-method' = 'Method';
'cs-uri-stem' = 'Stem';
'cs-uri-query' = 'Query';
's-port' = 'Port';
'cs-username' = 'UserName';
'c-ip' = 'ClientIP';
'cs-version' = 'Version';
'cs(User-Agent)' = 'UserAgent';
'cs(Cookie)' = 'Cookie';
'cs(Referer)' = 'Referer';
'cs-host' = 'Host';
'sc-status' = 'Status';
'sc-bytes' = 'BytesSent';
'cs-bytes' = 'BytesReceived';
'time-taken' = 'TimeTaken';
's-sitename' = 'SiteName';
's-computername' = 'ComputerName';
'sc-substatus' = 'Substatus';
'sc-win32-status' = 'Win32Status';
}

$script:milliseconds = [Collections.Generic.Hashset[String]]::New()
[void]$script:milliseconds.Add('time-taken')

$script:httpMethods = [Collections.Generic.Hashset[String]]::New()
[void]$script:httpMethods.Add('sc-method')

$srcPath = Join-Path -Path $script:moduleRoot -ChildPath 'src' -Resolve

Add-Type -Path (Get-ChildItem -Path $srcPath -Filter '*.cs').FullName `
-ReferencedAssemblies 'System.Net.Http','System.Net','System.Net.Primitives'

# Store each of your module's functions in its own file in the Functions
# directory. On the build server, your module's functions will be appended to
Expand Down
43 changes: 37 additions & 6 deletions W3CLogs/en-US/about_W3CLogs.help.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,39 @@
TOPIC
about_MODULE_NAME

SHORT DESCRIPTION
W3CLogs is a PowerShell module for...
# Overview

LONG DESCRIPTION
The W3CLogs PowerShell module...
The "W3CLogs" module has a single function `Import-W3CLog`, which parses and imports W3C log files.

# System Requirements

* Windows PowerShell 5.1 and .NET 4.6.1+
* PowerShell Core 6+

# Installing

To install globally:

```powershell
Install-Module -Name 'W3CLogs'
Import-Module -Name 'W3CLogs'
```

To install privately:

```powershell
Save-Module -Name 'W3CLogs' -Path '.'
Import-Module -Name '.\W3CLogs'
```

# Usage

Pass the path to a single log file to parse to the function's `Path` parameter:

```powershell
Import-W3CLog -Path 'log.log'
```

To parse multiple logs, pipe them in:

```powershell
Get-ChildItem -Recurse -Filter '*.log' | Import-W3CLog
```
Loading

0 comments on commit 9715218

Please sign in to comment.