logsloth.psm1
<#
.SYNOPSIS .EXAMPLE #> function logsloth { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelinebyPropertyName = $true)] [string] $LINE, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelinebyPropertyName = $true)] [switch] $SHOW_FUNC_COUNT = $false, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelinebyPropertyName = $true)] [switch] $RETURN_LOG_AS_TABLE = $false, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelinebyPropertyName = $true)] [switch] $OUT_AS_HTML = $false, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelinebyPropertyName = $true)] [int] $THREAD_INDENT = 4, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelinebyPropertyName = $true)] [int] $PERIOD_HIGHTLIGHT_MS = 5 ) begin { $_oldTime = $null $_oldTid = 0 $_threadIndentDic = @{} $_funcCountDic = @{} $_oldLog $_funcIndentDic = @{} $_oldfuncName = $null $_COLOR_LIST = @("Green", "DarkGray", "DarkGreen", "DarkYellow", "Gray", "Magenta", "Cyan", "DarkCyan", "DarkRed", "Yellow") $_oldLogLineNum = 0 if ($OUT_AS_HTML) { $_htmlFilePath = "logsloth-$([datetime]::Now.ToString("yyMMdd-HHmmss")).html" $_HTML_FILE_TOP | Out-File -Append $_htmlFilePath } } process { # Write-Host "LINE : $LINE" $logLineNum = $_oldLogLineNum + 1 $_oldLogLineNum = $logLineNum $canParse = $LINE -match "\d\d-\d\d\s\d\d:\d\d:\d\d.\d\d\d" if (!$canParse) { Write-Host $LINE -ForegroundColor White if ($OUT_AS_HTML) { $htmlRow = $LINE $htmlRow | Out-File $_htmlFilePath -Append } return } $curTime = [DateTime]::Parse("2018-$($matches[0])") if ($_oldTime -eq $null) { $_oldTime = $curTime } $period = $curTime - $_oldTime $_oldTime = $curTime $lineTokens = $LINE.Split(" ", [System.StringSplitOptions]::RemoveEmptyEntries) $tid = $lineTokens[3] $tag = $lineTokens[5] $funcName = $null $lineNum = 0 $fileName = $null $simpleFuncName = $null for ( $i = 0; $i -lt $lineTokens.Length; $i++ ) { $token = $lineTokens[$i] if ($token -match "[.cpp|.h|.cc|.c|.cxx|.hpp|.java]:\d+:") { $res = $token -split ":" $fileName = $res[0] $lineNum = $res[1] $simpleFuncName = $res[2] $funcName = "$($res[0])::$($res[2])" break } } if ($funcName -eq $null) { $funcName = "$tag$([string]::Join( " ", $lineTokens[5..6] ))" } $remainLog = [string]::Join(" ", $lineTokens[7..$($lineTokens.Length)] ) $isLeave = $false $funcIndent = 0 if ($_funcIndentDic.ContainsKey($tid)) { $funcIndent = $_funcIndentDic[$tid] } else { $funcIndent = -1 $_funcIndentDic[$tid] = $funcIndent } if ($LINE -like "*↘↘↘*") { $funcIndent = $funcIndent + 1 $_funcIndentDic[$tid] = $funcIndent } if ($LINE -like "*↗↗↗*") { $_funcIndentDic[$tid] = $funcIndent - 1 $isLeave = $true } $funcIndent = [System.Math]::Max($funcIndent, 0) # Write-Host "funcIndent[$funcIndent]" $isNewTid = $tid -ne $_oldTid $_oldTid = $tid $canSkipFuncName = ($_oldfuncName -eq $funcName) -and (!$isNewTid) $_oldfuncName = $funcName $callerFuncName = $null $isCallerLog = $LINE -match "→→→.*→→→" if ($isCallerLog) { $callerFuncName = $matches[0].Replace("→→→", "") } $calleeFuncName = $null $isCalleeLog = $LINE -match "←←←.*←←←" if ($isCalleeLog) { $calleeFuncName = $matches[0].Replace("←←←", "") } if (!$_funcCountDic.ContainsKey($funcName)) { $_funcCountDic[$funcName] = 1 } else { $_funcCountDic[$funcName] = $_funcCountDic[$funcName] + 1 } $padCount = 0 if (!$_threadIndentDic.ContainsKey($tid)) { $_threadIndentDic[$tid] = $_threadIndentDic.Count } $padCount = ($_threadIndentDic[$tid] % 10) * $THREAD_INDENT + 1 $leftPadding = " " * $padCount; $leftPadding += "·" * $funcIndent; $hashInt = _logsloth_str_2_byte_sum($funcName) $clrIdx = $hashInt % ($_COLOR_LIST.Length) $color = $_COLOR_LIST[$clrIdx]; $cmdRow = $null if ($period.TotalMilliseconds -ge $PERIOD_HIGHTLIGHT_MS) { $cmdRow += "`n`n`n`n`n" } if (!$canSkipFuncName) { $cmdRow += "`n" } if ($isNewTid) { $cmdRow += "TID[$tid]`n" } $cmdRow += "[$($lineTokens[0]) $($lineTokens[1])]" $cmdRow += "[$([string]::Format("{0,5:N0}", $period.TotalMilliseconds))ms]" $cmdRow += $leftPadding if (!$isLeave) { if ($canSkipFuncName) { $cmdRow += "`:$lineNum " } else { $cmdRow += "$fileName`:$lineNum`:$simpleFuncName " } } if ($isCallerLog) { $cmdRow += "→→→ $callerFuncName" } elseif ($isCalleeLog) { $cmdRow += "←←← $calleeFuncName" } else { $cmdRow += $remainLog } Write-Host $cmdRow -ForegroundColor $color if ($RETURN_LOG_AS_TABLE) { $row = New-Object psobject $row | Add-Member TimeStamp "$($lineTokens[0]) $($lineTokens[1])" $row | Add-Member Period "$([string]::Format("{0,5:N0}", $period.TotalMilliseconds))" $row | Add-Member TID $tid $row | Add-Member Color $color $row | Add-Member FileName $fileName $row | Add-Member LineNum $lineNum $row | Add-Member SimpleFuncName $simpleFuncName $row | Add-Member Log $remainLog $row | Add-Member OldLog $_oldLog $_oldLog = $remainLog return $row } if ($OUT_AS_HTML) { $htmlRow = $null if ($period.TotalMilliseconds -ge $PERIOD_HIGHTLIGHT_MS) { $htmlRow += "`n`n`n`n`n" } if (!$canSkipFuncName) { $htmlRow += "`n" } if ($isNewTid) { $htmlRow += "TID[$tid]`n" } $htmlRow += "<span style='color`: $color;'>" $htmlRow += "[$($lineTokens[0]) $($lineTokens[1])]" $htmlRow += "[$([string]::Format("{0,5:N0}", $period.TotalMilliseconds))ms]" $htmlRow += $leftPadding if (!$isLeave) { if ($canSkipFuncName) { $htmlRow += "`:$lineNum " } else { if ($isCalleeLog) { $htmlRow += "$fileName`:$lineNum`:$simpleFuncName " } else { $htmlRow += "$fileName`:$lineNum`:<span class='funcNameEnter'>$simpleFuncName</span> " } } } if ($isCallerLog) { $htmlRow += "<span class='funcNameCall'>$callerFuncName</span>" } elseif ($isCalleeLog) { $htmlRow += "<span class='funcNameEnter'>$calleeFuncName</span>" } else { $htmlRow += $remainLog } $htmlRow += "</span>" $htmlRow | Out-File $_htmlFilePath -Append } } end { if ($SHOW_FUNC_COUNT) { $_funcCountDic.Keys | Sort-Object -Descending { $funcName = $_ $funcCount = $_funcCountDic[$funcName] return $funcCount } | foreach { $funcName = $_ $funcCount = $_funcCountDic[$funcName] $statLog = "$([string]::Format("{0,-100} | {1,10}", $funcName, $funcCount))" Write-Host $statLog } # foreach } # if if ($OUT_AS_HTML) { $_HTML_FILE_BOTTOM | Out-File -Append $_htmlFilePath } } # end } function _logsloth_str_2_byte_sum { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelinebyPropertyName = $true)] [string] $STR ) $strBuf = [System.Text.Encoding]::UTF8.GetBytes($STR) $byteSum = 0 foreach ($byte in $strBuf) { $byteSum += $byte } return $byteSum } $_HTML_FILE_TOP = @" <html> <head> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <!-- https://github.com/a115/HTML-SVG-connect --> <script src="https://hhdpublish.blob.core.windows.net/pub/LeaticIcible/jquery.html-svg-connect.js"></script> <style> pre { color: white; background-color: black; font-family: 'Courier New', Courier, monospace; } .my-line-style { stroke-dasharray: 3px; stroke-width: 3px; } #svgContainer { z-index: 10; position: absolute; pointer-events:none; } </style> <script> function connectAll() { let paths = []; let funcNameCallArray = `$(".funcNameCall"); for (let i = 0; i < funcNameCallArray.length; i++) { let caller = funcNameCallArray.eq(i); let next = caller.parent().next(); for (let j = 0; j < 1000; j++) { if (next.find(".funcNameEnter").length > 0) { let callee = next.find(".funcNameEnter"); if (callee.text() == caller.text()) { let callerClr = caller.css("color"); caller.css("border-style", "solid").css("border-width", "1px"); callee.css("border-style", "solid").css("border-width", "1px"); paths.push({ start: caller, end: callee, stroke: callerClr }); break; } } next = next.next(); if (next.length == 0) { break; } } } `$("#svgContainer").HTMLSVGconnect({ orientation: "auto", class: "my-line-style", paths: paths, }); } `$(function () { console.log("hello jq"); connectAll(); }); `$(window).resize(function () { connectAll(); }); </script> </head> <body> <div> <div id="svgContainer"></div> <pre> "@ $_HTML_FILE_BOTTOM = @" </pre> </body> </html> "@ |