|
| 1 | +#------------------------------------------------------------------------------------------------------------------------------------------------ |
| 2 | +# helper function to generate color based on the job statu |
| 3 | +# this may need renaming to Get-DbaTimelineStatusColor or similar |
| 4 | +#------------------------------------------------------------------------------------------------------------------------------------------------ |
| 5 | +Function Get-StatusColor ([string]$Status) { |
| 6 | + $out = switch($Status){ |
| 7 | + "Failed" {"#FF3D3D"} |
| 8 | + "Succeeded" {"#36B300"} |
| 9 | + "Retry" {"#FFFF00"} |
| 10 | + "Canceled" {"#C2C2C2"} |
| 11 | + "In Progress" {"#00CCFF"} |
| 12 | + default {"#FF00CC"} |
| 13 | + } |
| 14 | + return $out |
| 15 | +} |
| 16 | + |
| 17 | +#------------------------------------------------------------------------------------------------------------------------------------------------ |
| 18 | +# helper function to convert human time to java script time format: new Date( Year, Month, Day, Hour, Minute, Second) |
| 19 | +# Java script time is zero base which means January = 0 and December = 11 |
| 20 | +#------------------------------------------------------------------------------------------------------------------------------------------------ |
| 21 | +Function ConvertTo-JsDate ([datetime]$InputDate) { |
| 22 | + $out = "new Date($(Get-Date $InputDate -format "yyyy"), $($(Get-Date $InputDate -format "MM")-1), $(Get-Date $InputDate -format "dd"), $(Get-Date $InputDate -format "HH"), $(Get-Date $InputDate -format "mm"), $(Get-Date $InputDate -format "ss"))" |
| 23 | + return $out |
| 24 | +} |
| 25 | + |
| 26 | +#------------------------------------------------------------------------------------------------------------------------------------------------ |
| 27 | +# function to convert to timeline |
| 28 | +#------------------------------------------------------------------------------------------------------------------------------------------------ |
| 29 | +Function ConvertTo-DbaTimeline { |
| 30 | + Param ( |
| 31 | + [parameter(Mandatory = $true, ValueFromPipeline = $true)] |
| 32 | + [object[]]$InputObject |
| 33 | + ) |
| 34 | + |
| 35 | + begin { |
| 36 | + #need to capture calling process to know what we are being asked for i.e. JobHistory, BackupHistory etc? |
| 37 | + #I dont know of any way apart from Get-PSCallStack but that return the whole stack but in order to the last |
| 38 | + #function should be the one that called this one? Not sure if this is correct but it works. |
| 39 | + $caller = Get-PSCallStack | Select -Property * | Select -last 1 |
| 40 | + #build html container |
| 41 | +@" |
| 42 | +<html> |
| 43 | +<head> |
| 44 | +<!-- Developed by Marcin Gminski, https://marcin.gminski.net, 2018 --> |
| 45 | + |
| 46 | +<!-- Load jQuery required to autosize timeline --> |
| 47 | +<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script> |
| 48 | + |
| 49 | +<!-- Load Bootstrap --> |
| 50 | +<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script> |
| 51 | +<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> |
| 52 | +<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous"> |
| 53 | + |
| 54 | +<!-- Load Google Charts library --> |
| 55 | +<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script> |
| 56 | + |
| 57 | +<!-- a bit of custom styling to work with bootstrap grid --> |
| 58 | +<style> |
| 59 | + |
| 60 | + html,body{height:100%;background-color:#c2c2c2;} |
| 61 | + |
| 62 | + .viewport {height:100%} |
| 63 | + |
| 64 | + .chart{ |
| 65 | + background-color:#fff; |
| 66 | + text-align:left; |
| 67 | + padding:0; |
| 68 | + border:1px solid #7D7D7D; |
| 69 | + -webkit-box-shadow:1px 1px 3px 0 rgba(0,0,0,.45); |
| 70 | + -moz-box-shadow:1px 1px 3px 0 rgba(0,0,0,.45); |
| 71 | + box-shadow:1px 1px 3px 0 rgba(0,0,0,.45) |
| 72 | + } |
| 73 | + .badge-custom{background-color:#939} |
| 74 | + |
| 75 | + .container { |
| 76 | + height:100%; |
| 77 | + } |
| 78 | + |
| 79 | + .fill{ |
| 80 | + width:100%; |
| 81 | + height:100%; |
| 82 | + min-height:100%; |
| 83 | + padding:10px; |
| 84 | + } |
| 85 | + |
| 86 | + .timeline-tooltip{ |
| 87 | + border:1px solid #E0E0E0; |
| 88 | + font-family:Arial,Helvetica; |
| 89 | + font-size:10pt; |
| 90 | + padding:12px |
| 91 | + } |
| 92 | + .timeline-tooltip div{padding:6px} |
| 93 | + .timeline-tooltip span{font-weight:700} |
| 94 | + |
| 95 | +</style> |
| 96 | + <script type="text/javascript"> |
| 97 | + google.charts.load('43', {'packages':['timeline']}); |
| 98 | + google.charts.setOnLoadCallback(drawChart); |
| 99 | + |
| 100 | + function drawChart() { |
| 101 | + var container = document.getElementById('Chart'); |
| 102 | + var chart = new google.visualization.Timeline(container); |
| 103 | + var dataTable = new google.visualization.DataTable(); |
| 104 | + dataTable.addColumn({type: 'string', id: 'vLabel'}); |
| 105 | + dataTable.addColumn({type: 'string', id: 'hLabel'}); |
| 106 | + dataTable.addColumn({type: 'string', role: 'style' }); |
| 107 | + dataTable.addColumn({type: 'date', id: 'date_start'}); |
| 108 | + dataTable.addColumn({type: 'date', id: 'date_end'}); |
| 109 | + |
| 110 | + dataTable.addRows([ |
| 111 | +"@ |
| 112 | + } |
| 113 | + |
| 114 | + process |
| 115 | + { |
| 116 | + $BaseObject = $InputObject.PsObject.BaseObject |
| 117 | + #This is where do column mapping: |
| 118 | + if ($caller.Position -Like "*Get-DbaAgentJobHistory*") { |
| 119 | + $CallerName = "Get-DbaAgentJobHistory" |
| 120 | + $data = $input | Select @{ Name="SqlInstance"; Expression = {$_.SqlInstance}}, @{ Name="InstanceName"; Expression = {$_.InstanceName}}, @{ Name="vLabel"; Expression = {$_.Job} }, @{ Name="hLabel"; Expression = {$_.Status} }, @{ Name="Style"; Expression = {$(Get-StatusColor($_.Status))} }, @{ Name="StartDate"; Expression = {$(ConvertTo-JsDate($_.StartDate))} }, @{ Name="EndDate"; Expression = {$(ConvertTo-JsDate($_.EndDate))} } |
| 121 | + |
| 122 | + } |
| 123 | + |
| 124 | + if ($caller.Position -Like "*Get-DbaBackupHistory*") { |
| 125 | + $CallerName = "Get-DbaBackupHistory" |
| 126 | + $data = $input | Select @{ Name="SqlInstance"; Expression = {$_.SqlInstance}}, @{ Name="InstanceName"; Expression = {$_.InstanceName}}, @{ Name="vLabel"; Expression = {$_.Database} }, @{ Name="hLabel"; Expression = {$_.Type} }, @{ Name="StartDate"; Expression = {$(ConvertTo-JsDate($_.Start))} }, @{ Name="EndDate"; Expression = {$(ConvertTo-JsDate($_.End))} } |
| 127 | + } |
| 128 | + "$( $data | %{"['$($_.vLabel)','$($_.hLabel)','$($_.Style)',$($_.StartDate), $($_.EndDate)],"})" |
| 129 | + } |
| 130 | + end { |
| 131 | +@" |
| 132 | +]); |
| 133 | + var paddingHeight = 20; |
| 134 | + var rowHeight = dataTable.getNumberOfRows() * 41; |
| 135 | + var chartHeight = rowHeight + paddingHeight; |
| 136 | + |
| 137 | + dataTable.insertColumn(2, {type: 'string', role: 'tooltip', p: {html: true}}); |
| 138 | + |
| 139 | + var dateFormat = new google.visualization.DateFormat({ |
| 140 | + pattern: 'dd/MM/yy HH:mm:ss' |
| 141 | + }); |
| 142 | + |
| 143 | + for (var i = 0; i < dataTable.getNumberOfRows(); i++) { |
| 144 | + var duration = (dataTable.getValue(i, 5).getTime() - dataTable.getValue(i, 4).getTime()) / 1000; |
| 145 | + var hours = parseInt( duration / 3600 ) % 24; |
| 146 | + var minutes = parseInt( duration / 60 ) % 60; |
| 147 | + var seconds = duration % 60; |
| 148 | + |
| 149 | + var tooltip = '<div class="timeline-tooltip"><span>' + |
| 150 | + dataTable.getValue(i, 1).split(",").join("<br />") + '</span></div><div class="timeline-tooltip"><span>' + |
| 151 | + dataTable.getValue(i, 0) + '</span>: ' + |
| 152 | + dateFormat.formatValue(dataTable.getValue(i, 4)) + ' - ' + |
| 153 | + dateFormat.formatValue(dataTable.getValue(i, 5)) + '</div>' + |
| 154 | + '<div class="timeline-tooltip"><span>Duration: </span>' + |
| 155 | + hours + 'h ' + minutes + 'm ' + seconds + 's '; |
| 156 | + |
| 157 | + dataTable.setValue(i, 2, tooltip); |
| 158 | + } |
| 159 | + |
| 160 | + var options = { |
| 161 | + timeline: { |
| 162 | + rowLabelStyle: { }, |
| 163 | + barLabelStyle: { }, |
| 164 | + }, |
| 165 | + hAxis: { |
| 166 | + format: 'dd/MM HH:mm', |
| 167 | + |
| 168 | + }, |
| 169 | + } |
| 170 | + |
| 171 | + // Autosize chart. It would not be enough to just count rows and expand based on row height as there can be overlappig rows. |
| 172 | + // this will draw the chart, get the size of the underlying div and apply that size to the parent container and redraw: |
| 173 | + chart.draw(dataTable, options); |
| 174 | + // get the size of the chold div: |
| 175 | + var realheight= parseInt(`$("#Chart div:first-child div:first-child div:first-child div svg").attr( "height"))+70; |
| 176 | + // set the height: |
| 177 | + options.height=realheight |
| 178 | + // draw again: |
| 179 | + chart.draw(dataTable, options); |
| 180 | + |
| 181 | + } |
| 182 | +</script> |
| 183 | +</head> |
| 184 | +<body> |
| 185 | + <div class="container-fluid"> |
| 186 | + <div class="pull-left"><h3><code>$($CallerName)</code> timeline for server <code>$($BaseObject.SqlInstance)</code></h3></div><div class="pull-right text-right"><img class="text-right" style="vertical-align:bottom; margin-top: 10px;" src="https://dbatools.io/wp-content/uploads/2016/05/dbatools-logo-1.png" width=150></div> |
| 187 | + <div class="clearfix"></div> |
| 188 | + <div class="col-12"> |
| 189 | + <div class="chart" id="Chart"></div> |
| 190 | + </div> |
| 191 | + <hr> |
| 192 | + <p><a href="https://dbatools.io">dbatools.io</a> - the community's sql powershell module. Find us on Twitter: <a href="https://twitter.com/psdbatools">@psdbatools</a> | Chart by <a href="https://twitter.com/marcingminski">@marcingminski</a></p> |
| 193 | +</div> |
| 194 | +</body> |
| 195 | +</html> |
| 196 | +"@ |
| 197 | + } |
| 198 | +} |
| 199 | + |
| 200 | +#execution |
| 201 | +#Get-DbaAgentJobHistory -SqlInstance sql-1 -StartDate ‘2018年08月13日 00:00’ -EndDate ‘2018年08月13日 23:59’ -NoJobSteps | ConvertTo-DbaTimeline | Out-File C:\temp\DbaAgentJobHistory.html -Encoding ASCII |
| 202 | +#Get-DbaBackupHistory -SqlInstance sql-1 -Since ‘2018年08月13日 00:00’ | ConvertTo-DbaTimeline | Out-File C:\temp\DbaBackupHistory.html -Encoding ASCII |
0 commit comments