├── .gitignore ├── assets ├── bold.png ├── logo_640x640.png ├── screen1_1280x800.png ├── screen2_1280x800.png ├── screen3_1280x800.png ├── screen4_1280x800.png ├── screen5_1280x800.png ├── screen_banner_1400x560.png ├── screen_banner_440x280.png └── screen_banner_920x680.png ├── img ├── logo_128.png ├── logo_16.png ├── logo_32.png └── logo_64.png ├── manifest.json ├── hello.html ├── LICENSE ├── DetailedDescription.md ├── README.md ├── colorize.js └── lib └── ansi_up.js /.gitignore: -------------------------------------------------------------------------------- 1 | .history 2 | archieve 3 | *.zip 4 | .idea 5 | -------------------------------------------------------------------------------- /assets/bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhan-mstf/colorize_cloudwatch_logs/HEAD/assets/bold.png -------------------------------------------------------------------------------- /img/logo_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhan-mstf/colorize_cloudwatch_logs/HEAD/img/logo_128.png -------------------------------------------------------------------------------- /img/logo_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhan-mstf/colorize_cloudwatch_logs/HEAD/img/logo_16.png -------------------------------------------------------------------------------- /img/logo_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhan-mstf/colorize_cloudwatch_logs/HEAD/img/logo_32.png -------------------------------------------------------------------------------- /img/logo_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhan-mstf/colorize_cloudwatch_logs/HEAD/img/logo_64.png -------------------------------------------------------------------------------- /assets/logo_640x640.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhan-mstf/colorize_cloudwatch_logs/HEAD/assets/logo_640x640.png -------------------------------------------------------------------------------- /assets/screen1_1280x800.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhan-mstf/colorize_cloudwatch_logs/HEAD/assets/screen1_1280x800.png -------------------------------------------------------------------------------- /assets/screen2_1280x800.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhan-mstf/colorize_cloudwatch_logs/HEAD/assets/screen2_1280x800.png -------------------------------------------------------------------------------- /assets/screen3_1280x800.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhan-mstf/colorize_cloudwatch_logs/HEAD/assets/screen3_1280x800.png -------------------------------------------------------------------------------- /assets/screen4_1280x800.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhan-mstf/colorize_cloudwatch_logs/HEAD/assets/screen4_1280x800.png -------------------------------------------------------------------------------- /assets/screen5_1280x800.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhan-mstf/colorize_cloudwatch_logs/HEAD/assets/screen5_1280x800.png -------------------------------------------------------------------------------- /assets/screen_banner_1400x560.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhan-mstf/colorize_cloudwatch_logs/HEAD/assets/screen_banner_1400x560.png -------------------------------------------------------------------------------- /assets/screen_banner_440x280.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhan-mstf/colorize_cloudwatch_logs/HEAD/assets/screen_banner_440x280.png -------------------------------------------------------------------------------- /assets/screen_banner_920x680.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhan-mstf/colorize_cloudwatch_logs/HEAD/assets/screen_banner_920x680.png -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Colorize AWS CloudWatch Logs", 3 | "description" : "Group logs visually on AWS CloudWatch", 4 | "version": "0.5.1", 5 | "homepage_url": "https://github.com/ilhan-mstf/colorize_cloudwatch_logs", 6 | "manifest_version": 2, 7 | "icons": { 8 | "16": "img/logo_16.png", 9 | "32": "img/logo_32.png", 10 | "64": "img/logo_64.png", 11 | "128": "img/logo_128.png" 12 | }, 13 | "browser_action": { 14 | "default_popup": "hello.html", 15 | "default_icon": "img/logo_16.png" 16 | }, 17 | "content_scripts": [{ 18 | "matches": [ 19 | "https://*.console.aws.amazon.com/cloudwatch/*" 20 | ], 21 | "js": [ 22 | "lib/ansi_up.js", 23 | "colorize.js" 24 | ] 25 | }] 26 | } 27 | -------------------------------------------------------------------------------- /hello.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 |

Colorize CloudWatch Logs

15 |

Group logs visually on AWS CloudWatch

16 |

This extension adds background color to boundaries of AWS Lambda logs, changes text color of lines having ANSI terminal codes and makes bold "REPORT", "[ERROR]" and "[Error " logs.

17 |

There is a shortcut for fullscreen.

18 |

If you enable "Follow tail", it refreshes the logs periodically to load new items.

19 |

This extension doesn't collect any user and web page information. It only runs on AWS CloudWatch Logs web page. It is free to use.

20 |

Github project

21 |

v0.5.1

22 | 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2018 Mustafa İlhan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /DetailedDescription.md: -------------------------------------------------------------------------------- 1 | The purpose of this extension is to group logs visually on AWS CloudWatch. There are three rules: 2 | - Set a different background color for each log group of AWS Lambda invocation. Therefore, you can easily recognize beginning, body and end of the logs of the same invocation. 3 | - Set font weight of lines having `REPORT`, `[ERROR]` and `[Error ` keywords to bold. 4 | - Set text color of ANSI terminal codes in the logs. (by https://github.com/oguimbal) 5 | 6 | Further, there is two functionality for better log viewing experience (by https://github.com/oguimbal): 7 | - Switching to fullscreen, 8 | - Refreshing the logs periodically to load new items. 9 | 10 | This extension doesn't collect any user and web page information. It only runs on AWS CloudWatch Logs web page. It is free to use. 11 | 12 | Overhead is very low, colorize operation takes ~7 milliseconds, listen operation for new event logs takes just ~0.5 milliseconds in every second. 13 | 14 | Contributions are welcome. To contribute please visit project page: https://github.com/ilhan-mstf/colorize_cloudwatch_logs 15 | 16 | Release Log: 17 | -------------------- 18 | Version 0.5.1: 19 | - Enable font override on new ui 20 | 21 | Version 0.5.0: 22 | - Handle new ui 23 | - Case insensitive error line search 24 | 25 | Version 0.4.11: 26 | - Add `[Error ` as a special line to make it bold 27 | - Add "Fullscreen" and "Follow tail" buttons 28 | 29 | Version 0.3.8: 30 | - Set text color of ANSI terminal codes 31 | - Update extension logo 32 | - Improve performance 33 | 34 | Version 0.2.1: 35 | - Make bold font weight of `REPORT` and `[ERROR]` lines -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Colorize CloudWatch Logs 2 | ======================== 3 | 4 | This is a Google Chrome extension. You can install this extension by this address: [Chrome Web Store](https://chrome.google.com/webstore/detail/colorize-cloudwatch-logs/fkagnmcbeokmapmcbecbcmpccmlbhkpl). 5 | 6 | The purpose of this extension is to group logs visually on AWS CloudWatch. There are three rules: 7 | - Set a different background color for each log group of AWS Lambda invocation. Therefore, you can easily recognize beginning, body and end of the logs of the same invocation. 8 | ![Grouping AWS Lambda log](assets/screen1_1280x800.png) 9 | - Set font wieght of lines having `REPORT`, `[ERROR]` and `[Error ` keywords to bold. 10 | ![Bold REPORT and ERROR log](assets/bold.png) 11 | - Set color of ANSI terminal codes in the logs. (by [@oguimbal](https://github.com/oguimbal)) 12 | ![Colorized ANSI terminal codes](assets/screen3_1280x800.png) 13 | 14 | Further, there is two functionality for better log viewing experience (by [@oguimbal](https://github.com/oguimbal)): 15 | - Switching to fullscreen, 16 | - Refreshing the logs periodically to load new items. 17 | ![Fullscreen and Follow Tail](assets/screen5_1280x800.png) 18 | 19 | This extension doesn't collect any user and web page information. It only runs on AWS CloudWatch Logs web page. It is free to use. 20 | 21 | Overhead is very low, colorize operation takes ~7 milliseconds, listen operation for new event logs takes just ~0.5 milliseconds in every second. 22 | 23 | ### Contributions 24 | Contributions are welcome. Please follow the standart.js convention if you want to contribute. 25 | 26 | ### Contributors 27 | - [@oguimbal](https://github.com/oguimbal) 28 | - [@svikrant2014](https://github.com/svikrant2014) 29 | - [@ktwbc](https://github.com/ktwbc) 30 | 31 | ### Open Source 32 | - [ansi_up.js](http://github.com/drudru/ansi_up) -------------------------------------------------------------------------------- /colorize.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * License: MIT 3 | * Author: Mustafa İlhan, http://ilhan-mstf.github.io/ 4 | * Contributors: 5 | * - Olivier Guimbal, https://github.com/oguimbal 6 | * - Vikrant Sharma, https://github.com/svikrant2014 7 | * - Kris Thom White, https://github.com/ktwbc 8 | */ 9 | 10 | 'use strict' 11 | 12 | /* global AnsiUp, localStorage */ 13 | 14 | const colors = ["#F9DBBD", "#CDC7E5", "#F6A5A2", "#FFF399", "#C4F4C7", "#E8E1EF", "#D9FFF8", "#ADFFE5", "#E6DBD0", "#C7FFDA", "#FCA17D", "#FCD0A1", "#FAFFD8", "#A3F7B5", "#D8B4E2"] 15 | 16 | const ansiTransform = new AnsiUp() 17 | delete window.AnsiUp // just delete it so its hidden from global space 18 | 19 | function insertStylesheet () { 20 | // dont know why, but all "spans" that are insterted in cwdb-ellipsis are blinking 21 | // this class prevents that from happening 22 | const style = document.createElement('style') 23 | style.textContent = ` 24 | .ansiColorized span { 25 | -webkit-animation-name: unset !important; 26 | -moz-animation-name: unset !important; 27 | -ms-animation-name: unset !important; 28 | animation-name: unset !important; 29 | } 30 | 31 | /* The container */ 32 | .container-checkbox { 33 | display: block; 34 | position: relative; 35 | padding-left: 20px; 36 | cursor: pointer; 37 | font-size: 13px; 38 | -webkit-user-select: none; 39 | -moz-user-select: none; 40 | -ms-user-select: none; 41 | user-select: none; 42 | } 43 | 44 | /* Hide the browser's default checkbox */ 45 | .container-checkbox input { 46 | position: absolute; 47 | opacity: 0; 48 | cursor: pointer; 49 | } 50 | 51 | /* Create a custom checkbox */ 52 | .container-checkbox .checkmark { 53 | position: absolute; 54 | top: 0; 55 | left: 0; 56 | height: 16px; 57 | width: 16px; 58 | background-color: #eee; 59 | } 60 | 61 | /* On mouse-over, add a grey background color */ 62 | .container-checkbox:hover input ~ .checkmark { 63 | background-color: #ccc; 64 | } 65 | 66 | /* When the checkbox is checked, add a blue background */ 67 | .container-checkbox input:checked ~ .checkmark { 68 | background-color: #2196F3; 69 | } 70 | 71 | /* Create the checkmark/indicator (hidden when not checked) */ 72 | .container-checkbox .checkmark:after { 73 | content: ""; 74 | position: absolute; 75 | display: none; 76 | } 77 | 78 | /* Show the checkmark when checked */ 79 | .container-checkbox input:checked ~ .checkmark:after { 80 | display: block; 81 | } 82 | 83 | /* Style the checkmark/indicator */ 84 | .container-checkbox .checkmark:after { 85 | left: 4px; 86 | top: 0px; 87 | width: 5px; 88 | height: 10px; 89 | border: solid white; 90 | border-width: 0 3px 3px 0; 91 | -webkit-transform: rotate(45deg); 92 | -ms-transform: rotate(45deg); 93 | transform: rotate(45deg); 94 | } 95 | 96 | #logs-tweaker-panel { 97 | position: fixed; 98 | z-index: 1000000; 99 | bottom: 30px; 100 | right: 20px; 101 | background: white; 102 | border-radius: 4px; 103 | box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); 104 | padding: 5px; 105 | } 106 | 107 | .cwdb-log-viewer-table-container.fullscreen .cwdb-log-viewer-table-body { 108 | position: fixed; 109 | background: white; 110 | margin-top: 0; 111 | top: 40px; 112 | left: 0; 113 | bottom: 0; 114 | right: 0; 115 | height: unset; 116 | } 117 | 118 | .logs__log-events-table__cell { 119 | font-family: "Helvetica Neue", Roboto, Arial, sans-serif; 120 | font-size: 14px; 121 | } 122 | 123 | .logs__log-events-table__cursor-text { 124 | font-family: "Helvetica Neue", Roboto, Arial, sans-serif; 125 | font-size: 14px; 126 | } 127 | 128 | .logs__log-events-table__timestamp-cell { 129 | font-family: "Helvetica Neue", Roboto, Arial, sans-serif; 130 | font-size: 14px; 131 | } 132 | 133 | ` 134 | document.head.appendChild(style) 135 | } 136 | 137 | function insertTools () { 138 | const panel = document.createElement('div') 139 | let panelHtml = `
` 140 | if (isNewDesign()) { 141 | panelHtml = `${ panelHtml } 142 | 146 | ` 147 | } else { 148 | panelHtml = `${ panelHtml } 149 | 153 | 157 | ` 158 | } 159 | 160 | panel.innerHTML = panelHtml + '
' 161 | document.body.append(panel) 162 | 163 | if (isNewDesign()) { 164 | setupNewToggles() 165 | } else { 166 | setupOldToggles() 167 | } 168 | } 169 | 170 | function setupOldToggles () { 171 | // toggle fullscreen 172 | document.getElementById('logs-tweaker-fullscreen').onchange = t => { 173 | const { checked } = t.target 174 | if (checked) { 175 | localStorage.setItem('logs-tweaker-fullscreen', 'yes') 176 | } else { 177 | localStorage.removeItem('logs-tweaker-fullscreen') 178 | } 179 | refreshFullscreen() 180 | } 181 | 182 | // toggle auto refresh 183 | document.getElementById('logs-tweaker-autorefresh').onchange = t => { 184 | const { checked } = t.target 185 | if (checked) { 186 | localStorage.setItem('logs-tweaker-autorefresh', 'yes') 187 | } else { 188 | localStorage.removeItem('logs-tweaker-autorefresh') 189 | } 190 | refreshAutoRefresh() 191 | } 192 | 193 | } 194 | 195 | function setupNewToggles () { 196 | // toggle replace fonts 197 | document.getElementById('logs-tweaker-fonts').onchange = t => { 198 | const { checked } = t.target 199 | if (checked) { 200 | localStorage.setItem('logs-tweaker-fonts', 'yes') 201 | } else { 202 | localStorage.removeItem('logs-tweaker-fonts') 203 | } 204 | refreshFonts() 205 | } 206 | } 207 | 208 | function removeTools () { 209 | const element = document.getElementById('logs-tweaker-panel') 210 | if (element) { 211 | element.parentNode.removeChild(element) 212 | } 213 | } 214 | 215 | // add "auto refresh" & "fullscreen" 216 | setInterval(() => { 217 | if (window.location.hash.includes('#logEventViewer') || isNewDesign()) { 218 | if (!isNewDesign()) { 219 | refreshOldDesign() 220 | } else { 221 | refreshNewDesign() 222 | } 223 | if (document.getElementById('logs-tweaker-panel')) { 224 | return 225 | } 226 | insertStylesheet() 227 | insertTools() 228 | } else { 229 | removeTools() 230 | } 231 | }, 1000) 232 | 233 | function refreshOldDesign () { 234 | refreshAutoRefresh() 235 | refreshFullscreen() 236 | } 237 | 238 | function refreshNewDesign () { 239 | refreshAutoRefresh() 240 | refreshFullscreen() 241 | } 242 | 243 | function fullscreenOn () { 244 | return !!localStorage.getItem('logs-tweaker-fullscreen') 245 | } 246 | 247 | function fontsOn () { 248 | return !!localStorage.getItem('logs-tweaker-fonts') 249 | } 250 | 251 | function refreshFullscreen () { 252 | const elt = document.getElementsByClassName('cwdb-log-viewer-table-container')[0] 253 | if (!elt) { 254 | return 255 | } 256 | if (fullscreenOn()) { 257 | if (!elt.classList.contains('fullscreen')) { 258 | elt.classList.add('fullscreen') 259 | } 260 | } else { 261 | elt.classList.remove('fullscreen') 262 | } 263 | } 264 | 265 | function autorefreshOn () { 266 | return !!localStorage.getItem('logs-tweaker-autorefresh') 267 | } 268 | 269 | let autorefreshInterval = null 270 | 271 | function refreshAutoRefresh () { 272 | if (autorefreshOn()) { 273 | if (!autorefreshInterval) { 274 | autorefreshInterval = setInterval(refreshTail, 3000) 275 | refreshTail() 276 | } 277 | } else { 278 | clearInterval(autorefreshInterval) 279 | autorefreshInterval = null 280 | } 281 | } 282 | 283 | function refreshFonts () { 284 | const elements = getElements() 285 | elements.forEach(element => changeFontElement(element, fontsOn() ? 'set' : 'clear')) 286 | } 287 | 288 | function refreshTail () { 289 | const refresh = document.getElementsByClassName('cwdb-log-viewer-table-infinite-loader-bottom')[0] 290 | if (!refresh) { 291 | return 292 | } 293 | let a = refresh.firstElementChild 294 | while (a && a.tagName !== 'A') { 295 | a = a.nextElementSibling 296 | } 297 | if (a) { 298 | a.click() 299 | // scroll to bottom 300 | const div = document.getElementsByClassName('cwdb-log-viewer-table-body')[0] 301 | if (div) { 302 | div.scrollTop = div.scrollHeight 303 | } 304 | } 305 | } 306 | 307 | function isCheckedForDecorated (element) { 308 | return element.dataset.checkedForDecorated !== 'yes' 309 | } 310 | 311 | function setCheckedForDecorated (element) { 312 | element.dataset.checkedForDecorated = 'yes' 313 | return element 314 | } 315 | 316 | function isCheckedForBold (element) { 317 | return element.dataset.checkedForBold !== 'yes' 318 | } 319 | 320 | function setCheckedForBold (element) { 321 | element.dataset.checkedForBold = 'yes' 322 | return element 323 | } 324 | 325 | function isStartLine (element) { 326 | return element.innerHTML.includes('START RequestId:') 327 | } 328 | 329 | function isEndLine (element) { 330 | return element.innerHTML.includes('REPORT RequestId:') 331 | } 332 | 333 | function isErrorLine (element) { 334 | const text = element.innerHTML.toLowerCase() 335 | return text.includes('error') 336 | } 337 | 338 | function isErrorOrEndLine (element) { 339 | return isErrorLine(element) || isEndLine(element) 340 | } 341 | 342 | function isStartOrEnd (element) { 343 | return isStartLine(element) || isEndLine(element) 344 | } 345 | 346 | function hasId (element, id) { 347 | return element.innerHTML.includes(id) 348 | } 349 | 350 | function colorizeElement (element, color) { 351 | element.style.backgroundColor = color 352 | return element 353 | } 354 | 355 | function changeFontElement (element, action) { 356 | if (element.dataset.isFontHandled !== 'yes' || action) { 357 | element.dataset.isFontHandled = 'yes' 358 | element.height = '20px' 359 | element.lineHeight = '20px' 360 | 361 | let subElements = element.getElementsByClassName('logs__log-events-table__cell') 362 | for (let e of subElements) { 363 | if (action === 'clear') { 364 | e.style.fontFamily = null; 365 | e.style.fontSize = null; 366 | e.style.paddingLeft = null; 367 | } else { 368 | e.style.fontFamily = '"Helvetica Neue", Roboto, Arial, sans-serif' 369 | e.style.fontSize = '0.9em' 370 | e.style.paddingLeft = '5px' 371 | } 372 | } 373 | } 374 | return element 375 | } 376 | 377 | function makeBoldElement (element) { 378 | element.style.fontWeight = 600 379 | return element 380 | } 381 | 382 | function makeBold (elements) { 383 | elements 384 | .filter(isCheckedForBold) 385 | .map(setCheckedForBold) 386 | .filter(isErrorOrEndLine) 387 | .forEach(makeBoldElement) 388 | } 389 | 390 | function getEventId (element) { 391 | return element 392 | .innerHTML 393 | .replace(/\n/g, ' ') 394 | .match(/\w{8}-\w{4}-\w{4}-\w{4}-\w{12}/g)[0] 395 | } 396 | 397 | function getUniqueEventIds (eventIds) { 398 | return Array.from( 399 | new Set( 400 | eventIds 401 | .filter(isCheckedForDecorated) 402 | .map(setCheckedForDecorated) 403 | .filter(isStartOrEnd) 404 | .map(getEventId))) 405 | } 406 | 407 | function changeFontOnGroup (elements) { 408 | elements.forEach(changeFontElement) 409 | } 410 | 411 | function colorizeGroup (elements) { 412 | let color = colors[Math.floor((Math.random() * 10000)) % colors.length] 413 | elements.forEach(element => colorizeElement(element, color)) 414 | } 415 | 416 | function decorateGroups (elements) { 417 | let eventIds = getUniqueEventIds(elements) 418 | let newDesign = isNewDesign() 419 | if (eventIds) { 420 | eventIds.forEach( 421 | id => { 422 | colorizeGroup(elements.filter(element => hasId(element, id))) 423 | if (newDesign && fontsOn()) changeFontOnGroup(elements) 424 | }) 425 | } 426 | } 427 | 428 | function applyAnsiTransform (e) { 429 | if (e) { 430 | const txt = e.childNodes[0] 431 | const textValue = txt.textContent || '' 432 | if (/(^|\x1b)\[(\d+)m/.test(textValue)) { 433 | e.classList.add('ansiColorized') 434 | e.innerHTML = ansiTransform.ansi_to_html(textValue) 435 | } 436 | } 437 | } 438 | 439 | function colorizeAnsi (elements) { 440 | for (let e of elements) { 441 | if (e.dataset.isAnsiColorizedHandled !== 'yes') { 442 | e.dataset.isAnsiColorizedHandled = 'yes' 443 | applyAnsiTransform(e.getElementsByClassName("logs__log-events-table__cell")[1]) 444 | } 445 | } 446 | } 447 | 448 | function getElements () { 449 | let elements 450 | 451 | const newDesignElements = document.querySelectorAll('iframe#microConsole-Logs')[0] 452 | 453 | if (newDesignElements) { 454 | elements = newDesignElements.contentDocument.getElementsByClassName('awsui-table-row') 455 | } else { 456 | elements = document.getElementsByClassName('cwdb-ellipsis') 457 | } 458 | 459 | return [].slice.call(elements) 460 | } 461 | 462 | function isNewDesign () { 463 | return window.location.hash.includes('#logsV2:log-groups/log-group') && window.location.hash.includes('/log-events') 464 | } 465 | 466 | function colorizeAll () { 467 | // console.time('cost-of-colorize') 468 | // console.time('cost-of-getting-elements') 469 | const elements = getElements() 470 | // console.timeEnd('cost-of-getting-elements') 471 | 472 | // console.time('cost-of-colorize-groups') 473 | decorateGroups(elements) 474 | // console.timeEnd('cost-of-colorize-groups') 475 | 476 | // console.time('cost-of-colorize-ansi') 477 | colorizeAnsi(elements) 478 | // console.timeEnd('cost-of-colorize-ansi') 479 | 480 | // console.time('cost-of-bold') 481 | makeBold(elements) 482 | // console.timeEnd('cost-of-bold') 483 | // console.timeEnd('cost-of-colorize') 484 | } 485 | 486 | setInterval(colorizeAll, 1000) 487 | -------------------------------------------------------------------------------- /lib/ansi_up.js: -------------------------------------------------------------------------------- 1 | /* ansi_up.js 2 | * author : Dru Nelson 3 | * license : MIT 4 | * http://github.com/drudru/ansi_up 5 | */ 6 | (function (root, factory) { 7 | if (typeof define === 'function' && define.amd) { 8 | // AMD. Register as an anonymous module. 9 | define(['exports'], factory); 10 | } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') { 11 | // CommonJS 12 | factory(exports); 13 | } else { 14 | // Browser globals 15 | var exp = {}; 16 | factory(exp); 17 | root.AnsiUp = exp.default; 18 | } 19 | }(this, function (exports) { 20 | "use strict"; 21 | var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) { 22 | if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } 23 | return cooked; 24 | }; 25 | var PacketKind; 26 | (function (PacketKind) { 27 | PacketKind[PacketKind["EOS"] = 0] = "EOS"; 28 | PacketKind[PacketKind["Text"] = 1] = "Text"; 29 | PacketKind[PacketKind["Incomplete"] = 2] = "Incomplete"; 30 | PacketKind[PacketKind["ESC"] = 3] = "ESC"; 31 | PacketKind[PacketKind["Unknown"] = 4] = "Unknown"; 32 | PacketKind[PacketKind["SGR"] = 5] = "SGR"; 33 | PacketKind[PacketKind["OSCURL"] = 6] = "OSCURL"; 34 | })(PacketKind || (PacketKind = {})); 35 | var AnsiUp = (function () { 36 | function AnsiUp() { 37 | this.VERSION = "4.0.3"; 38 | this.setup_palettes(); 39 | this._use_classes = false; 40 | this._escape_for_html = true; 41 | this.bold = false; 42 | this.fg = this.bg = null; 43 | this._buffer = ''; 44 | this._url_whitelist = { 'http': 1, 'https': 1 }; 45 | } 46 | Object.defineProperty(AnsiUp.prototype, "use_classes", { 47 | get: function () { 48 | return this._use_classes; 49 | }, 50 | set: function (arg) { 51 | this._use_classes = arg; 52 | }, 53 | enumerable: true, 54 | configurable: true 55 | }); 56 | Object.defineProperty(AnsiUp.prototype, "escape_for_html", { 57 | get: function () { 58 | return this._escape_for_html; 59 | }, 60 | set: function (arg) { 61 | this._escape_for_html = arg; 62 | }, 63 | enumerable: true, 64 | configurable: true 65 | }); 66 | Object.defineProperty(AnsiUp.prototype, "url_whitelist", { 67 | get: function () { 68 | return this._url_whitelist; 69 | }, 70 | set: function (arg) { 71 | this._url_whitelist = arg; 72 | }, 73 | enumerable: true, 74 | configurable: true 75 | }); 76 | AnsiUp.prototype.setup_palettes = function () { 77 | var _this = this; 78 | this.ansi_colors = 79 | [ 80 | [ 81 | { rgb: [0, 0, 0], class_name: "ansi-black" }, 82 | { rgb: [187, 0, 0], class_name: "ansi-red" }, 83 | { rgb: [0, 187, 0], class_name: "ansi-green" }, 84 | { rgb: [187, 187, 0], class_name: "ansi-yellow" }, 85 | { rgb: [0, 0, 187], class_name: "ansi-blue" }, 86 | { rgb: [187, 0, 187], class_name: "ansi-magenta" }, 87 | { rgb: [0, 187, 187], class_name: "ansi-cyan" }, 88 | { rgb: [255, 255, 255], class_name: "ansi-white" } 89 | ], 90 | [ 91 | { rgb: [85, 85, 85], class_name: "ansi-bright-black" }, 92 | { rgb: [255, 85, 85], class_name: "ansi-bright-red" }, 93 | { rgb: [0, 255, 0], class_name: "ansi-bright-green" }, 94 | { rgb: [255, 255, 85], class_name: "ansi-bright-yellow" }, 95 | { rgb: [85, 85, 255], class_name: "ansi-bright-blue" }, 96 | { rgb: [255, 85, 255], class_name: "ansi-bright-magenta" }, 97 | { rgb: [85, 255, 255], class_name: "ansi-bright-cyan" }, 98 | { rgb: [255, 255, 255], class_name: "ansi-bright-white" } 99 | ] 100 | ]; 101 | this.palette_256 = []; 102 | this.ansi_colors.forEach(function (palette) { 103 | palette.forEach(function (rec) { 104 | _this.palette_256.push(rec); 105 | }); 106 | }); 107 | var levels = [0, 95, 135, 175, 215, 255]; 108 | for (var r = 0; r < 6; ++r) { 109 | for (var g = 0; g < 6; ++g) { 110 | for (var b = 0; b < 6; ++b) { 111 | var col = { rgb: [levels[r], levels[g], levels[b]], class_name: 'truecolor' }; 112 | this.palette_256.push(col); 113 | } 114 | } 115 | } 116 | var grey_level = 8; 117 | for (var i = 0; i < 24; ++i, grey_level += 10) { 118 | var gry = { rgb: [grey_level, grey_level, grey_level], class_name: 'truecolor' }; 119 | this.palette_256.push(gry); 120 | } 121 | }; 122 | AnsiUp.prototype.escape_txt_for_html = function (txt) { 123 | return txt.replace(/[&<>]/gm, function (str) { 124 | if (str === "&") 125 | return "&"; 126 | if (str === "<") 127 | return "<"; 128 | if (str === ">") 129 | return ">"; 130 | }); 131 | }; 132 | AnsiUp.prototype.append_buffer = function (txt) { 133 | var str = this._buffer + txt; 134 | this._buffer = str; 135 | }; 136 | AnsiUp.prototype.get_next_packet = function () { 137 | var pkt = { 138 | kind: PacketKind.EOS, 139 | text: '', 140 | url: '' 141 | }; 142 | var len = this._buffer.length; 143 | if (len == 0) 144 | return pkt; 145 | var pos = this._buffer.indexOf("\x1B"); 146 | if (pos == -1) { 147 | pkt.kind = PacketKind.Text; 148 | pkt.text = this._buffer; 149 | this._buffer = ''; 150 | return pkt; 151 | } 152 | if (pos > 0) { 153 | pkt.kind = PacketKind.Text; 154 | pkt.text = this._buffer.slice(0, pos); 155 | this._buffer = this._buffer.slice(pos); 156 | return pkt; 157 | } 158 | if (pos == 0) { 159 | if (len == 1) { 160 | pkt.kind = PacketKind.Incomplete; 161 | return pkt; 162 | } 163 | var next_char = this._buffer.charAt(1); 164 | if ((next_char != '[') && (next_char != ']')) { 165 | pkt.kind = PacketKind.ESC; 166 | pkt.text = this._buffer.slice(0, 1); 167 | this._buffer = this._buffer.slice(1); 168 | return pkt; 169 | } 170 | if (next_char == '[') { 171 | if (!this._csi_regex) { 172 | this._csi_regex = rgx(__makeTemplateObject(["\n ^ # beginning of line\n #\n # First attempt\n (?: # legal sequence\n \u001B[ # CSI\n ([<-?]?) # private-mode char\n ([d;]*) # any digits or semicolons\n ([ -/]? # an intermediate modifier\n [@-~]) # the command\n )\n | # alternate (second attempt)\n (?: # illegal sequence\n \u001B[ # CSI\n [ -~]* # anything legal\n ([\0-\u001F:]) # anything illegal\n )\n "], ["\n ^ # beginning of line\n #\n # First attempt\n (?: # legal sequence\n \\x1b\\[ # CSI\n ([\\x3c-\\x3f]?) # private-mode char\n ([\\d;]*) # any digits or semicolons\n ([\\x20-\\x2f]? # an intermediate modifier\n [\\x40-\\x7e]) # the command\n )\n | # alternate (second attempt)\n (?: # illegal sequence\n \\x1b\\[ # CSI\n [\\x20-\\x7e]* # anything legal\n ([\\x00-\\x1f:]) # anything illegal\n )\n "])); 173 | } 174 | var match = this._buffer.match(this._csi_regex); 175 | if (match === null) { 176 | pkt.kind = PacketKind.Incomplete; 177 | return pkt; 178 | } 179 | if (match[4]) { 180 | pkt.kind = PacketKind.ESC; 181 | pkt.text = this._buffer.slice(0, 1); 182 | this._buffer = this._buffer.slice(1); 183 | return pkt; 184 | } 185 | if ((match[1] != '') || (match[3] != 'm')) 186 | pkt.kind = PacketKind.Unknown; 187 | else 188 | pkt.kind = PacketKind.SGR; 189 | pkt.text = match[2]; 190 | var rpos = match[0].length; 191 | this._buffer = this._buffer.slice(rpos); 192 | return pkt; 193 | } 194 | if (next_char == ']') { 195 | if (len < 4) { 196 | pkt.kind = PacketKind.Incomplete; 197 | return pkt; 198 | } 199 | if ((this._buffer.charAt(2) != '8') 200 | || (this._buffer.charAt(3) != ';')) { 201 | pkt.kind = PacketKind.ESC; 202 | pkt.text = this._buffer.slice(0, 1); 203 | this._buffer = this._buffer.slice(1); 204 | return pkt; 205 | } 206 | if (!this._osc_st) { 207 | this._osc_st = rgxG(__makeTemplateObject(["\n (?: # legal sequence\n (\u001B\\) # ESC | # alternate\n (\u0007) # BEL (what xterm did)\n )\n | # alternate (second attempt)\n ( # illegal sequence\n [\0-\u0006] # anything illegal\n | # alternate\n [\b-\u001A] # anything illegal\n | # alternate\n [\u001C-\u001F] # anything illegal\n )\n "], ["\n (?: # legal sequence\n (\\x1b\\\\) # ESC \\\n | # alternate\n (\\x07) # BEL (what xterm did)\n )\n | # alternate (second attempt)\n ( # illegal sequence\n [\\x00-\\x06] # anything illegal\n | # alternate\n [\\x08-\\x1a] # anything illegal\n | # alternate\n [\\x1c-\\x1f] # anything illegal\n )\n "])); 208 | } 209 | this._osc_st.lastIndex = 0; 210 | { 211 | var match_1 = this._osc_st.exec(this._buffer); 212 | if (match_1 === null) { 213 | pkt.kind = PacketKind.Incomplete; 214 | return pkt; 215 | } 216 | if (match_1[3]) { 217 | pkt.kind = PacketKind.ESC; 218 | pkt.text = this._buffer.slice(0, 1); 219 | this._buffer = this._buffer.slice(1); 220 | return pkt; 221 | } 222 | } 223 | { 224 | var match_2 = this._osc_st.exec(this._buffer); 225 | if (match_2 === null) { 226 | pkt.kind = PacketKind.Incomplete; 227 | return pkt; 228 | } 229 | if (match_2[3]) { 230 | pkt.kind = PacketKind.ESC; 231 | pkt.text = this._buffer.slice(0, 1); 232 | this._buffer = this._buffer.slice(1); 233 | return pkt; 234 | } 235 | } 236 | if (!this._osc_regex) { 237 | this._osc_regex = rgx(__makeTemplateObject(["\n ^ # beginning of line\n #\n \u001B]8; # OSC Hyperlink\n [ -:<-~]* # params (excluding ;)\n ; # end of params\n ([!-~]{0,512}) # URL capture\n (?: # ST\n (?:\u001B\\) # ESC | # alternate\n (?:\u0007) # BEL (what xterm did)\n )\n ([!-~]+) # TEXT capture\n \u001B]8;; # OSC Hyperlink End\n (?: # ST\n (?:\u001B\\) # ESC | # alternate\n (?:\u0007) # BEL (what xterm did)\n )\n "], ["\n ^ # beginning of line\n #\n \\x1b\\]8; # OSC Hyperlink\n [\\x20-\\x3a\\x3c-\\x7e]* # params (excluding ;)\n ; # end of params\n ([\\x21-\\x7e]{0,512}) # URL capture\n (?: # ST\n (?:\\x1b\\\\) # ESC \\\n | # alternate\n (?:\\x07) # BEL (what xterm did)\n )\n ([\\x21-\\x7e]+) # TEXT capture\n \\x1b\\]8;; # OSC Hyperlink End\n (?: # ST\n (?:\\x1b\\\\) # ESC \\\n | # alternate\n (?:\\x07) # BEL (what xterm did)\n )\n "])); 238 | } 239 | var match = this._buffer.match(this._osc_regex); 240 | if (match === null) { 241 | pkt.kind = PacketKind.ESC; 242 | pkt.text = this._buffer.slice(0, 1); 243 | this._buffer = this._buffer.slice(1); 244 | return pkt; 245 | } 246 | pkt.kind = PacketKind.OSCURL; 247 | pkt.url = match[1]; 248 | pkt.text = match[2]; 249 | var rpos = match[0].length; 250 | this._buffer = this._buffer.slice(rpos); 251 | return pkt; 252 | } 253 | } 254 | }; 255 | AnsiUp.prototype.ansi_to_html = function (txt) { 256 | this.append_buffer(txt); 257 | var blocks = []; 258 | while (true) { 259 | var packet = this.get_next_packet(); 260 | if ((packet.kind == PacketKind.EOS) 261 | || (packet.kind == PacketKind.Incomplete)) 262 | break; 263 | if ((packet.kind == PacketKind.ESC) 264 | || (packet.kind == PacketKind.Unknown)) 265 | continue; 266 | if (packet.kind == PacketKind.Text) 267 | blocks.push(this.transform_to_html(this.with_state(packet))); 268 | else if (packet.kind == PacketKind.SGR) 269 | this.process_ansi(packet); 270 | else if (packet.kind == PacketKind.OSCURL) 271 | blocks.push(this.process_hyperlink(packet)); 272 | } 273 | return blocks.join(""); 274 | }; 275 | AnsiUp.prototype.with_state = function (pkt) { 276 | return { bold: this.bold, fg: this.fg, bg: this.bg, text: pkt.text }; 277 | }; 278 | AnsiUp.prototype.process_ansi = function (pkt) { 279 | var sgr_cmds = pkt.text.split(';'); 280 | while (sgr_cmds.length > 0) { 281 | var sgr_cmd_str = sgr_cmds.shift(); 282 | var num = parseInt(sgr_cmd_str, 10); 283 | if (isNaN(num) || num === 0) { 284 | this.fg = this.bg = null; 285 | this.bold = false; 286 | } 287 | else if (num === 1) { 288 | this.bold = true; 289 | } 290 | else if (num === 22) { 291 | this.bold = false; 292 | } 293 | else if (num === 39) { 294 | this.fg = null; 295 | } 296 | else if (num === 49) { 297 | this.bg = null; 298 | } 299 | else if ((num >= 30) && (num < 38)) { 300 | this.fg = this.ansi_colors[0][(num - 30)]; 301 | } 302 | else if ((num >= 40) && (num < 48)) { 303 | this.bg = this.ansi_colors[0][(num - 40)]; 304 | } 305 | else if ((num >= 90) && (num < 98)) { 306 | this.fg = this.ansi_colors[1][(num - 90)]; 307 | } 308 | else if ((num >= 100) && (num < 108)) { 309 | this.bg = this.ansi_colors[1][(num - 100)]; 310 | } 311 | else if (num === 38 || num === 48) { 312 | if (sgr_cmds.length > 0) { 313 | var is_foreground = (num === 38); 314 | var mode_cmd = sgr_cmds.shift(); 315 | if (mode_cmd === '5' && sgr_cmds.length > 0) { 316 | var palette_index = parseInt(sgr_cmds.shift(), 10); 317 | if (palette_index >= 0 && palette_index <= 255) { 318 | if (is_foreground) 319 | this.fg = this.palette_256[palette_index]; 320 | else 321 | this.bg = this.palette_256[palette_index]; 322 | } 323 | } 324 | if (mode_cmd === '2' && sgr_cmds.length > 2) { 325 | var r = parseInt(sgr_cmds.shift(), 10); 326 | var g = parseInt(sgr_cmds.shift(), 10); 327 | var b = parseInt(sgr_cmds.shift(), 10); 328 | if ((r >= 0 && r <= 255) && (g >= 0 && g <= 255) && (b >= 0 && b <= 255)) { 329 | var c = { rgb: [r, g, b], class_name: 'truecolor' }; 330 | if (is_foreground) 331 | this.fg = c; 332 | else 333 | this.bg = c; 334 | } 335 | } 336 | } 337 | } 338 | } 339 | }; 340 | AnsiUp.prototype.transform_to_html = function (fragment) { 341 | var txt = fragment.text; 342 | if (txt.length === 0) 343 | return txt; 344 | if (this._escape_for_html) 345 | txt = this.escape_txt_for_html(txt); 346 | if (!fragment.bold && fragment.fg === null && fragment.bg === null) 347 | return txt; 348 | var styles = []; 349 | var classes = []; 350 | var fg = fragment.fg; 351 | var bg = fragment.bg; 352 | if (fragment.bold) 353 | styles.push('font-weight:bold'); 354 | if (!this._use_classes) { 355 | if (fg) 356 | styles.push("color:rgb(" + fg.rgb.join(',') + ")"); 357 | if (bg) 358 | styles.push("background-color:rgb(" + bg.rgb + ")"); 359 | } 360 | else { 361 | if (fg) { 362 | if (fg.class_name !== 'truecolor') { 363 | classes.push(fg.class_name + "-fg"); 364 | } 365 | else { 366 | styles.push("color:rgb(" + fg.rgb.join(',') + ")"); 367 | } 368 | } 369 | if (bg) { 370 | if (bg.class_name !== 'truecolor') { 371 | classes.push(bg.class_name + "-bg"); 372 | } 373 | else { 374 | styles.push("background-color:rgb(" + bg.rgb.join(',') + ")"); 375 | } 376 | } 377 | } 378 | var class_string = ''; 379 | var style_string = ''; 380 | if (classes.length) 381 | class_string = " class=\"" + classes.join(' ') + "\""; 382 | if (styles.length) 383 | style_string = " style=\"" + styles.join(';') + "\""; 384 | return "" + txt + ""; 385 | }; 386 | ; 387 | AnsiUp.prototype.process_hyperlink = function (pkt) { 388 | var parts = pkt.url.split(':'); 389 | if (parts.length < 1) 390 | return ''; 391 | if (!this._url_whitelist[parts[0]]) 392 | return ''; 393 | var result = "" + this.escape_txt_for_html(pkt.text) + ""; 394 | return result; 395 | }; 396 | return AnsiUp; 397 | }()); 398 | function rgx(tmplObj) { 399 | var subst = []; 400 | for (var _i = 1; _i < arguments.length; _i++) { 401 | subst[_i - 1] = arguments[_i]; 402 | } 403 | var regexText = tmplObj.raw[0]; 404 | var wsrgx = /^\s+|\s+\n|\s*#[\s\S]*?\n|\n/gm; 405 | var txt2 = regexText.replace(wsrgx, ''); 406 | return new RegExp(txt2); 407 | } 408 | function rgxG(tmplObj) { 409 | var subst = []; 410 | for (var _i = 1; _i < arguments.length; _i++) { 411 | subst[_i - 1] = arguments[_i]; 412 | } 413 | var regexText = tmplObj.raw[0]; 414 | var wsrgx = /^\s+|\s+\n|\s*#[\s\S]*?\n|\n/gm; 415 | var txt2 = regexText.replace(wsrgx, ''); 416 | return new RegExp(txt2, 'g'); 417 | } 418 | //# sourceMappingURL=ansi_up.js.map 419 | Object.defineProperty(exports, "__esModule", { value: true }); 420 | exports.default = AnsiUp; 421 | })); --------------------------------------------------------------------------------