Public/Convert-PatternLayout.ps1
function Convert-PatternLayout { [OutputType([System.Text.RegularExpressions.Regex])] [CmdletBinding()] param ( [string] $PatternLayout = '%timestamp [%thread] %level %logger %ndc - %message%newline' # This is the DetailPattern https://logging.apache.org/log4net/release/sdk/?topic=html/T_log4net_Layout_PatternLayout.htm#Remarks ) # Regex to identiy pattern names # Conversions are '%' + formater + conversion pattern name # Formatter start with `.` or `-` and numbers or range # https://rubular.com/r/r9jtIzuxJag0HV [regex]$patternRegex = '%(?<right_justify>-)?(?<min_width>\d+)?\.?(?<max_width>\d+)?(?<name>\w+)' # This wonky replace replaces any special characters so they don't mess up the regex later $regExString = "^" + ($PatternLayout -replace '([\\\*\+\?\|\{\}\[\]\(,)\^\$\.\#]\B)', '\$1' ) + "$" $conversions = $patternRegex.Matches($PatternLayout) foreach ($conversion in $conversions) { $name = $conversion.Groups['name'].Value $conRegex = switch ($name) { '%' { '%' } { @('appdomain', 'a') -contains $_ } { '\w+' } { @('logger', 'c') -contains $_ } { '\w+' } { @('type', 'C', 'class') -contains $_ } { '\w+' } { @('date', 'd', 'utcdate') -contains $_ } { # This has it's own modifiers. '\d{4}-\d{2}-\d{2} [0-9:]{8},\d{3}' } { @('file', 'F') -contains $_ } { '\w+' } { @('level', 'p') -contains $_ } { '\w+' } { @('location', 'l') -contains $_ } { '\w+' } { @('line', 'L') -contains $_ } { '\w+' } { @('message', 'm') -contains $_ } { '.*' } { @('method', 'M') -contains $_ } { '\w+' } { @('newline', 'n') -contains $_ } { '\n' } { @('property', 'properties', 'P') -contains $_ } { '\w+' } { @('timestamp', 'r') -contains $_ } { '\d+' } { @('thread', 't') -contains $_ } { '\d+' } { @('identity', 'u') -contains $_ } { '\w+' } { @('mdc', 'X') -contains $_ } { '\w+' } { @('ndc', 'x') -contains $_ } { '\w+' } { @('username', 'w') -contains $_ } { '\w+' } 'aspnet-cache' { '(?<aspnet-cache>\w+)' } 'aspnet-context' { '(?<aspnet-context>\w+)' } 'aspnet-request' { '(?<aspnet-request>\w+)' } 'aspnet-session' { '(?<aspnet-session>\w+)' } 'exception' { '\w+' } 'stacktrace' { '\w+' } 'stacktracedetail' { '\w+' } Default { throw "Unknown conversion pattern name: $name" } } # If we detected any of the formatting bits, let's use them. if ( $conversion.Groups['right_justify'].Success -Or $conversion.Groups['min_width'].Success -Or $conversion.Groups['max_width'].Success ) { $formatString = '{' # Determine the padding/truncating if ($conversion.Groups['min_width'].Success) { $formatString += $conversion.Groups['min_width'].Value } else { $formatString += '0' } $formatString += ',' if ($conversion.Groups['max_width'].Success) { $formatString += $conversion.Groups['max_width'].Value } else { # Nothing, this means it can be as long as it wants } $formatString += '}' # Determine where to add the space if ($conversion.Groups['right_justify'].Success) { $regExGroup = "(?<{0}>(?=.{2}\B){1}\s*)" -F $name, $conRegex, $formatString } else { # If given only a max width & no right justify then there is no spacing # This means truncate after N letters. if ($conversion.Groups['min_width'].Success -eq $False) { $regExGroup = "(?<{0}>(?=.{2}\B){1})" -F $name, $conRegex, $formatString } else { $regExGroup = "(?<{0}>(?=.{2}\B)\s*{1})" -F $name, $conRegex, $formatString } } } else { $regExGroup = "(?<{0}>{1})" -F $name, $conRegex } # Replace the original pattern with our regex $regExString = $regExString.replace($conversion.Value, $regExGroup) } return [regex]$regExString } |