logster 2.21.0 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +24 -0
  3. data/README.md +29 -14
  4. data/assets/javascript/main-C9t0S9He.js +1 -0
  5. data/assets/javascript/modules-4-12-BnbZfQQN.js +1 -0
  6. data/assets/javascript/vendor.js +11 -5260
  7. data/assets/logster-config.json +18 -0
  8. data/assets/manifest.json +23 -0
  9. data/assets/stylesheets/main-et5OPj6E.css +1 -0
  10. data/lib/logster/configuration.rb +1 -0
  11. data/lib/logster/middleware/reporter.rb +34 -3
  12. data/lib/logster/middleware/viewer.rb +204 -32
  13. data/lib/logster/rails/railtie.rb +4 -20
  14. data/lib/logster/redis_store.rb +0 -2
  15. data/lib/logster/version.rb +1 -1
  16. data/lib/logster.rb +1 -1
  17. metadata +17 -186
  18. data/.github/workflows/ci.yml +0 -140
  19. data/.gitignore +0 -3
  20. data/.rubocop.yml +0 -12
  21. data/.streerc +0 -2
  22. data/Gemfile +0 -10
  23. data/Guardfile +0 -10
  24. data/Rakefile +0 -24
  25. data/assets/javascript/.gitkeep +0 -0
  26. data/assets/javascript/chunk.524.b7d0b89d25457a72409c.js +0 -22
  27. data/assets/javascript/chunk.582.5a04ae4713e692cd335a.js +0 -22
  28. data/assets/javascript/chunk.747.a730c497582bb6742a6e.js +0 -579
  29. data/assets/javascript/chunk.761.64949d89942c829e5c1b.js +0 -1213
  30. data/assets/javascript/client-app.js +0 -1389
  31. data/assets/stylesheets/.gitkeep +0 -0
  32. data/assets/stylesheets/client-app.css +0 -1
  33. data/assets/stylesheets/vendor.css +0 -1
  34. data/build_client_app.sh +0 -10
  35. data/client-app/.editorconfig +0 -19
  36. data/client-app/.ember-cli +0 -9
  37. data/client-app/.eslintignore +0 -20
  38. data/client-app/.eslintrc +0 -10
  39. data/client-app/.gitignore +0 -23
  40. data/client-app/.prettierignore +0 -21
  41. data/client-app/.prettierrc +0 -1
  42. data/client-app/.template-lintrc.js +0 -10
  43. data/client-app/.watchmanconfig +0 -3
  44. data/client-app/README.md +0 -56
  45. data/client-app/app/app.js +0 -12
  46. data/client-app/app/components/actions-menu.js +0 -50
  47. data/client-app/app/components/back-to-site-link.js +0 -9
  48. data/client-app/app/components/back-trace.js +0 -153
  49. data/client-app/app/components/env-tab.js +0 -68
  50. data/client-app/app/components/message-info.js +0 -151
  51. data/client-app/app/components/message-row.js +0 -46
  52. data/client-app/app/components/page-nav.js +0 -42
  53. data/client-app/app/components/panel-resizer.js +0 -108
  54. data/client-app/app/components/patterns-list.js +0 -142
  55. data/client-app/app/components/tab-contents.js +0 -23
  56. data/client-app/app/components/tabbed-section.js +0 -45
  57. data/client-app/app/components/time-formatter.js +0 -36
  58. data/client-app/app/controllers/index.js +0 -288
  59. data/client-app/app/controllers/show.js +0 -39
  60. data/client-app/app/helpers/logster-url.js +0 -12
  61. data/client-app/app/helpers/or.js +0 -7
  62. data/client-app/app/index.html +0 -31
  63. data/client-app/app/initializers/app-init.js +0 -71
  64. data/client-app/app/lib/decorators.js +0 -13
  65. data/client-app/app/lib/preload.js +0 -43
  66. data/client-app/app/lib/utilities.js +0 -230
  67. data/client-app/app/models/group.js +0 -34
  68. data/client-app/app/models/message-collection.js +0 -303
  69. data/client-app/app/models/message.js +0 -128
  70. data/client-app/app/models/pattern-item.js +0 -32
  71. data/client-app/app/resolver.js +0 -3
  72. data/client-app/app/router.js +0 -13
  73. data/client-app/app/routes/index.js +0 -64
  74. data/client-app/app/routes/settings.js +0 -32
  75. data/client-app/app/routes/show.js +0 -16
  76. data/client-app/app/services/events.js +0 -4
  77. data/client-app/app/styles/app.css +0 -898
  78. data/client-app/app/templates/application.hbs +0 -1
  79. data/client-app/app/templates/components/actions-menu.hbs +0 -31
  80. data/client-app/app/templates/components/back-to-site-link.hbs +0 -8
  81. data/client-app/app/templates/components/back-trace.hbs +0 -15
  82. data/client-app/app/templates/components/env-tab.hbs +0 -10
  83. data/client-app/app/templates/components/message-info.hbs +0 -86
  84. data/client-app/app/templates/components/message-row.hbs +0 -32
  85. data/client-app/app/templates/components/page-nav.hbs +0 -37
  86. data/client-app/app/templates/components/panel-resizer.hbs +0 -3
  87. data/client-app/app/templates/components/patterns-list.hbs +0 -78
  88. data/client-app/app/templates/components/tabbed-section.hbs +0 -18
  89. data/client-app/app/templates/components/time-formatter.hbs +0 -1
  90. data/client-app/app/templates/index.hbs +0 -192
  91. data/client-app/app/templates/settings.hbs +0 -47
  92. data/client-app/app/templates/show.hbs +0 -13
  93. data/client-app/config/ember-cli-update.json +0 -18
  94. data/client-app/config/environment.js +0 -51
  95. data/client-app/config/icons.js +0 -28
  96. data/client-app/config/optional-features.json +0 -6
  97. data/client-app/config/targets.js +0 -26
  98. data/client-app/ember-cli-build.js +0 -34
  99. data/client-app/package.json +0 -76
  100. data/client-app/preload-json-manager.rb +0 -35
  101. data/client-app/public/assets/images/icon_144x144.png +0 -0
  102. data/client-app/public/assets/images/icon_64x64.png +0 -0
  103. data/client-app/testem.js +0 -23
  104. data/client-app/tests/index.html +0 -41
  105. data/client-app/tests/integration/components/back-to-site-link-test.js +0 -20
  106. data/client-app/tests/integration/components/back-trace-test.js +0 -138
  107. data/client-app/tests/integration/components/env-tab-test.js +0 -220
  108. data/client-app/tests/integration/components/message-info-test.js +0 -122
  109. data/client-app/tests/integration/components/patterns-list-test.js +0 -76
  110. data/client-app/tests/test-helper.js +0 -12
  111. data/client-app/tests/unit/controllers/index-test.js +0 -77
  112. data/client-app/tests/unit/controllers/show-test.js +0 -12
  113. data/client-app/tests/unit/routes/index-test.js +0 -11
  114. data/client-app/tests/unit/routes/show-test.js +0 -11
  115. data/client-app/yarn.lock +0 -9905
  116. data/gemfiles/rails_6.1.gemfile +0 -12
  117. data/gemfiles/rails_7.0.gemfile +0 -12
  118. data/gemfiles/rails_7.1.gemfile +0 -9
  119. data/gemfiles/rails_7.2.gemfile +0 -9
  120. data/logster.gemspec +0 -43
  121. data/test/dummy/config/application.rb +0 -31
  122. data/test/dummy/config/boot.rb +0 -6
  123. data/test/dummy/config/environment.rb +0 -6
  124. data/test/examples/test_sidekiq_reporter_example.rb +0 -47
  125. data/test/fake_data/Gemfile +0 -6
  126. data/test/fake_data/generate.rb +0 -23
  127. data/test/logster/middleware/test_reporter.rb +0 -84
  128. data/test/logster/middleware/test_viewer.rb +0 -495
  129. data/test/logster/test_base_store.rb +0 -224
  130. data/test/logster/test_cache.rb +0 -39
  131. data/test/logster/test_defer_logger.rb +0 -36
  132. data/test/logster/test_group.rb +0 -92
  133. data/test/logster/test_ignore_pattern.rb +0 -42
  134. data/test/logster/test_logger.rb +0 -177
  135. data/test/logster/test_message.rb +0 -269
  136. data/test/logster/test_pattern.rb +0 -144
  137. data/test/logster/test_railtie.rb +0 -21
  138. data/test/logster/test_redis_rate_limiter.rb +0 -246
  139. data/test/logster/test_redis_store.rb +0 -1165
  140. data/test/test_helper.rb +0 -66
@@ -0,0 +1,18 @@
1
+ {
2
+ "modulePrefix": "client-app",
3
+ "environment": "production",
4
+ "rootURL": "/logs",
5
+ "locationType": "history",
6
+ "EmberENV": {
7
+ "FEATURES": {},
8
+ "EXTEND_PROTOTYPES": {
9
+ "Date": false
10
+ },
11
+ "_APPLICATION_TEMPLATE_WRAPPER": false,
12
+ "_DEFAULT_ASYNC_OBSERVERS": true,
13
+ "_JQUERY_INTEGRATION": false,
14
+ "_TEMPLATE_ONLY_GLIMMER_COMPONENTS": true,
15
+ "_USE_EMBER_MODULES": true
16
+ },
17
+ "APP": {}
18
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "index.html": {
3
+ "file": "assets/main-C9t0S9He.js",
4
+ "name": "main",
5
+ "src": "index.html",
6
+ "isEntry": true,
7
+ "dynamicImports": [
8
+ "node_modules/.pnpm/@embroider+legacy-inspector-support@0.1.3/node_modules/@embroider/legacy-inspector-support/src/modules-4-12.js"
9
+ ],
10
+ "css": [
11
+ "assets/main-et5OPj6E.css"
12
+ ]
13
+ },
14
+ "node_modules/.pnpm/@embroider+legacy-inspector-support@0.1.3/node_modules/@embroider/legacy-inspector-support/src/modules-4-12.js": {
15
+ "file": "assets/modules-4-12-BnbZfQQN.js",
16
+ "name": "modules-4-12",
17
+ "src": "node_modules/.pnpm/@embroider+legacy-inspector-support@0.1.3/node_modules/@embroider/legacy-inspector-support/src/modules-4-12.js",
18
+ "isDynamicEntry": true,
19
+ "imports": [
20
+ "index.html"
21
+ ]
22
+ }
23
+ }
@@ -0,0 +1 @@
1
+ :root,:host{--fa-font-solid:normal 900 1em/1 "Font Awesome 7 Free";--fa-font-regular:normal 400 1em/1 "Font Awesome 7 Free";--fa-font-light:normal 300 1em/1 "Font Awesome 7 Pro";--fa-font-thin:normal 100 1em/1 "Font Awesome 7 Pro";--fa-font-duotone:normal 900 1em/1 "Font Awesome 7 Duotone";--fa-font-duotone-regular:normal 400 1em/1 "Font Awesome 7 Duotone";--fa-font-duotone-light:normal 300 1em/1 "Font Awesome 7 Duotone";--fa-font-duotone-thin:normal 100 1em/1 "Font Awesome 7 Duotone";--fa-font-brands:normal 400 1em/1 "Font Awesome 7 Brands";--fa-font-sharp-solid:normal 900 1em/1 "Font Awesome 7 Sharp";--fa-font-sharp-regular:normal 400 1em/1 "Font Awesome 7 Sharp";--fa-font-sharp-light:normal 300 1em/1 "Font Awesome 7 Sharp";--fa-font-sharp-thin:normal 100 1em/1 "Font Awesome 7 Sharp";--fa-font-sharp-duotone-solid:normal 900 1em/1 "Font Awesome 7 Sharp Duotone";--fa-font-sharp-duotone-regular:normal 400 1em/1 "Font Awesome 7 Sharp Duotone";--fa-font-sharp-duotone-light:normal 300 1em/1 "Font Awesome 7 Sharp Duotone";--fa-font-sharp-duotone-thin:normal 100 1em/1 "Font Awesome 7 Sharp Duotone";--fa-font-slab-regular:normal 400 1em/1 "Font Awesome 7 Slab";--fa-font-slab-press-regular:normal 400 1em/1 "Font Awesome 7 Slab Press";--fa-font-slab-duo-regular:normal 400 1em/1 "Font Awesome 7 Slab Duo";--fa-font-slab-press-duo-regular:normal 400 1em/1 "Font Awesome 7 Slab Press Duo";--fa-font-pixel-regular:normal 400 1em/1 "Font Awesome 7 Pixel";--fa-font-mosaic-solid:normal 900 1em/1 "Font Awesome 7 Mosaic";--fa-font-vellum-solid:normal 900 1em/1 "Font Awesome 7 Vellum";--fa-font-whiteboard-semibold:normal 600 1em/1 "Font Awesome 7 Whiteboard";--fa-font-thumbprint-light:normal 300 1em/1 "Font Awesome 7 Thumbprint";--fa-font-notdog-solid:normal 900 1em/1 "Font Awesome 7 Notdog";--fa-font-notdog-duo-solid:normal 900 1em/1 "Font Awesome 7 Notdog Duo";--fa-font-etch-solid:normal 900 1em/1 "Font Awesome 7 Etch";--fa-font-graphite-thin:normal 100 1em/1 "Font Awesome 7 Graphite";--fa-font-jelly-regular:normal 400 1em/1 "Font Awesome 7 Jelly";--fa-font-jelly-fill-regular:normal 400 1em/1 "Font Awesome 7 Jelly Fill";--fa-font-jelly-duo-regular:normal 400 1em/1 "Font Awesome 7 Jelly Duo";--fa-font-chisel-regular:normal 400 1em/1 "Font Awesome 7 Chisel";--fa-font-utility-semibold:normal 600 1em/1 "Font Awesome 7 Utility";--fa-font-utility-duo-semibold:normal 600 1em/1 "Font Awesome 7 Utility Duo";--fa-font-utility-fill-semibold:normal 600 1em/1 "Font Awesome 7 Utility Fill"}.svg-inline--fa{box-sizing:content-box;display:var(--fa-display,inline-block);vertical-align:-.125em;height:1em;width:var(--fa-width,1.25em);overflow:visible}.svg-inline--fa.fa-2xs{vertical-align:.1em}.svg-inline--fa.fa-xs{vertical-align:0}.svg-inline--fa.fa-sm{vertical-align:-.0714286em}.svg-inline--fa.fa-lg{vertical-align:-.2em}.svg-inline--fa.fa-xl{vertical-align:-.25em}.svg-inline--fa.fa-2xl{vertical-align:-.3125em}.svg-inline--fa.fa-pull-left,.svg-inline--fa .fa-pull-start{float:inline-start;margin-inline-end:var(--fa-pull-margin,.3em)}.svg-inline--fa.fa-pull-right,.svg-inline--fa .fa-pull-end{float:inline-end;margin-inline-start:var(--fa-pull-margin,.3em)}.svg-inline--fa.fa-li{width:var(--fa-li-width,2em);inset-block-start:.25em;inset-inline-start:calc(-1 * var(--fa-li-width,2em))}.fa-layers-counter,.fa-layers-text{text-align:center;display:inline-block;position:absolute}.fa-layers{text-align:center;vertical-align:-.125em;height:1em;width:var(--fa-width,1.25em);display:inline-block;position:relative}.fa-layers .svg-inline--fa{transform-origin:50%;margin:auto;position:absolute;inset:0}.fa-layers-text{transform-origin:50%;top:50%;left:50%;transform:translate(-50%,-50%)}.fa-layers-counter{background-color:var(--fa-counter-background-color,#ff253a);border-radius:var(--fa-counter-border-radius,1em);box-sizing:border-box;color:var(--fa-inverse,#fff);line-height:var(--fa-counter-line-height,1);max-width:var(--fa-counter-max-width,5em);min-width:var(--fa-counter-min-width,1.5em);padding:var(--fa-counter-padding,.25em .5em);right:var(--fa-right,0);text-overflow:ellipsis;top:var(--fa-top,0);transform:scale(var(--fa-counter-scale,.25));transform-origin:100% 0;overflow:hidden}.fa-layers-bottom-right{bottom:var(--fa-bottom,0);right:var(--fa-right,0);transform:scale(var(--fa-layers-scale,.25));transform-origin:100% 100%;top:auto}.fa-layers-bottom-left{bottom:var(--fa-bottom,0);left:var(--fa-left,0);transform:scale(var(--fa-layers-scale,.25));transform-origin:0 100%;top:auto;right:auto}.fa-layers-top-right{top:var(--fa-top,0);right:var(--fa-right,0);transform:scale(var(--fa-layers-scale,.25));transform-origin:100% 0}.fa-layers-top-left{left:var(--fa-left,0);right:auto;top:var(--fa-top,0);transform:scale(var(--fa-layers-scale,.25));transform-origin:0 0}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-2xs{vertical-align:.225em;font-size:.625em;line-height:.1em}.fa-xs{vertical-align:.125em;font-size:.75em;line-height:.0833333em}.fa-sm{vertical-align:.0535715em;font-size:.875em;line-height:.0714286em}.fa-lg{vertical-align:-.075em;font-size:1.25em;line-height:.05em}.fa-xl{vertical-align:-.125em;font-size:1.5em;line-height:.0416667em}.fa-2xl{vertical-align:-.1875em;font-size:2em;line-height:.03125em}.fa-width-auto{--fa-width:auto}.fa-fw,.fa-width-fixed{--fa-width:1.25em}.fa-canvas-square{margin-block-end:-.125em;padding-block:.125em}.fa-canvas-roomy{box-sizing:content-box;margin-block-end:-.25em;padding-block:.25em;padding-inline:.125em}.fa-ul{margin-inline-start:var(--fa-li-margin,2.5em);padding-inline-start:0;list-style-type:none}.fa-ul>li{position:relative}.fa-li{text-align:center;width:var(--fa-li-width,2em);line-height:inherit;position:absolute;inset-inline-start:calc(-1 * var(--fa-li-width,2em))}.fa-border{border-color:var(--fa-border-color,#eee);border-radius:var(--fa-border-radius,.1em);border-style:var(--fa-border-style,solid);border-width:var(--fa-border-width,.0625em);box-sizing:var(--fa-border-box-sizing,content-box);padding:var(--fa-border-padding,.1875em .25em)}.fa-pull-left,.fa-pull-start{float:inline-start;margin-inline-end:var(--fa-pull-margin,.3em)}.fa-pull-right,.fa-pull-end{float:inline-end;margin-inline-start:var(--fa-pull-margin,.3em)}.fa-beat{animation-name:fa-beat;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-bounce{animation-name:fa-bounce;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,cubic-bezier(.28, .84, .42, 1))}.fa-fade{animation-name:fa-fade;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-beat-fade{animation-name:fa-beat-fade;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-flip{animation-name:fa-flip;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1.5s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-flip-360{animation-name:fa-flip-360;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-shake{animation-name:fa-shake;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,.75s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-spin{animation-name:fa-spin;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,2s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,linear)}.fa-spin-reverse{--fa-animation-direction:reverse}.fa-pulse,.fa-spin-pulse{animation-name:fa-spin;animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,steps(8))}.fa-spin-snap{animation-name:fa-spin-snap;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,3s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,linear)}.fa-spin-snap-4{animation-name:fa-spin-snap-4;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,2.4s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,linear)}.fa-spin-snap-8{animation-name:fa-spin-snap-8;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,4s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,linear)}.fa-buzz{animation-name:fa-buzz;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,.6s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,linear)}.fa-wag{animation-name:fa-wag;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,.9s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,ease-out);transform-origin:bottom}.fa-float{animation-name:fa-float;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,3s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,ease-in-out);will-change:transform}.fa-swing{animation-name:fa-swing;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1.2s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,ease-out);transform-origin:top}.fa-jello{animation-name:fa-jello;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,.9s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,ease-out)}@media (prefers-reduced-motion:reduce){.fa-beat,.fa-bounce,.fa-fade,.fa-beat-fade,.fa-flip,.fa-flip-360,.fa-pulse,.fa-shake,.fa-spin,.fa-spin-pulse,.fa-buzz,.fa-float,.fa-jello,.fa-spin-snap,.fa-spin-snap-4,.fa-spin-snap-8,.fa-swing,.fa-wag{transition:none!important;animation:none!important}}@keyframes fa-beat{0%{transform:scale(1)}25%{transform:scale(calc(1.25 * var(--fa-beat-scale,1.25)))}45%{transform:scale(calc(1.22 * var(--fa-beat-scale,1.22)))}65%{transform:scale(calc(1.25 * var(--fa-beat-scale,1.25)))}90%{transform:scale(1)}}@keyframes fa-bounce{0%{animation-timing-function:var(--fa-animation-timing);transform:scale(1)translateY(0)}14%{transform:scale(var(--fa-bounce-start-scale-x,1.06), var(--fa-bounce-start-scale-y,.94)) translateY(var(--fa-bounce-anticipation,3px));animation-timing-function:cubic-bezier(.33,0,.66,.33)}32%{transform:scale(var(--fa-bounce-jump-scale-x,.94), var(--fa-bounce-jump-scale-y,1.12)) translateY(calc(-1 * var(--fa-bounce-height,.5em)));animation-timing-function:cubic-bezier(.33,.66,.66,1)}52%{transform:scale(1, 1) translateY(calc(-1 * var(--fa-bounce-height,.5em) * 1.1));animation-timing-function:cubic-bezier(.5,0,1,.5)}70%{transform:scale(var(--fa-bounce-land-scale-x,1.06), var(--fa-bounce-land-scale-y,.92)) translateY(0);animation-timing-function:cubic-bezier(.33,.33,.66,1)}85%{transform:scale(.98, 1.04) translateY(calc(-2px * var(--fa-bounce-rebound,1)));animation-timing-function:cubic-bezier(.33,0,.66,1)}to{transform:scale(1)translateY(0)}}@keyframes fa-fade{0%{opacity:1;animation-timing-function:cubic-bezier(.2,0,.4,1);transform:scale(1)}40%{opacity:var(--fa-fade-opacity,.4);animation-timing-function:cubic-bezier(.4,0,.6,1);transform:scale(.98)}to{opacity:1;transform:scale(1)}}@keyframes fa-beat-fade{0%{opacity:var(--fa-beat-fade-opacity,.4);animation-timing-function:cubic-bezier(.2,0,.4,1);transform:scale(1)}25%{opacity:calc(var(--fa-beat-fade-opacity,.4) + .4);transform:scale(var(--fa-beat-fade-scale,1.28));animation-timing-function:cubic-bezier(.4,0,.6,1)}45%{opacity:1;transform:scale(var(--fa-beat-fade-scale,1.25));animation-timing-function:cubic-bezier(.4,0,.2,1)}65%{opacity:calc(var(--fa-beat-fade-opacity,.4) + .4);transform:scale(var(--fa-beat-fade-scale,1.28));animation-timing-function:cubic-bezier(.4,0,.6,1)}to{opacity:var(--fa-beat-fade-opacity,.4);transform:scale(1)}}@keyframes fa-flip{0%{transform:perspective(2em) scale(1) rotate3d(var(--fa-flip-x,0), var(--fa-flip-y,1), var(--fa-flip-z,0), 0deg);animation-timing-function:cubic-bezier(.2,0,.4,1)}8%{transform:perspective(2em) scale(var(--fa-flip-anticipation-scale,.95)) rotate3d(var(--fa-flip-x,0), var(--fa-flip-y,1), var(--fa-flip-z,0), 0deg);animation-timing-function:cubic-bezier(.33,0,.66,.33)}35%{transform:perspective(2em) scale(1) rotate3d(var(--fa-flip-x,0), var(--fa-flip-y,1), var(--fa-flip-z,0), calc(var(--fa-flip-angle,-360deg) * .6));animation-timing-function:linear}65%{transform:perspective(2em) scale(1) rotate3d(var(--fa-flip-x,0), var(--fa-flip-y,1), var(--fa-flip-z,0), calc(var(--fa-flip-angle,-360deg) * .5));animation-timing-function:cubic-bezier(.33,.66,.66,1)}92%{transform:perspective(2em) scale(1) rotate3d(var(--fa-flip-x,0), var(--fa-flip-y,1), var(--fa-flip-z,0), calc(var(--fa-flip-angle,-360deg) * var(--fa-flip-overshoot,1.04)));animation-timing-function:cubic-bezier(.33,0,.66,1)}to{transform:perspective(2em) scale(1) rotate3d(var(--fa-flip-x,0), var(--fa-flip-y,1), var(--fa-flip-z,0), var(--fa-flip-angle,-360deg))}}@keyframes fa-flip-360{0%{transform:perspective(2em) scale(1) rotate3d(var(--fa-flip-x,0), var(--fa-flip-y,1), var(--fa-flip-z,0), 0deg);animation-timing-function:cubic-bezier(.2,0,.4,1)}8%{transform:perspective(2em) scale(var(--fa-flip-anticipation-scale,.95)) rotate3d(var(--fa-flip-x,0), var(--fa-flip-y,1), var(--fa-flip-z,0), 0deg);animation-timing-function:cubic-bezier(.33,0,.66,.33)}50%{transform:perspective(2em) scale(1) rotate3d(var(--fa-flip-x,0), var(--fa-flip-y,1), var(--fa-flip-z,0), calc(var(--fa-flip-angle,-360deg) * .6));animation-timing-function:cubic-bezier(.33,.66,.66,1)}80%{transform:perspective(2em) scale(1) rotate3d(var(--fa-flip-x,0), var(--fa-flip-y,1), var(--fa-flip-z,0), calc(var(--fa-flip-angle,-360deg) * var(--fa-flip-overshoot,1.04)));animation-timing-function:cubic-bezier(.33,0,.66,1)}to{transform:perspective(2em) scale(1) rotate3d(var(--fa-flip-x,0), var(--fa-flip-y,1), var(--fa-flip-z,0), var(--fa-flip-angle,-360deg))}}@keyframes fa-shake{0%{animation-timing-function:cubic-bezier(.2,0,.8,1);transform:rotate(0)}8%{animation-timing-function:cubic-bezier(.3,0,.7,1);transform:rotate(35deg)translate(1px)}20%{animation-timing-function:cubic-bezier(.3,0,.7,1);transform:rotate(-22deg)translate(-1px)}35%{animation-timing-function:cubic-bezier(.3,0,.7,1);transform:rotate(15deg)translate(1px)}50%{animation-timing-function:cubic-bezier(.4,0,.6,1);transform:rotate(-9deg)}65%{animation-timing-function:cubic-bezier(.4,0,.6,1);transform:rotate(5deg)}78%{animation-timing-function:cubic-bezier(.4,0,.6,1);transform:rotate(-3deg)}90%{animation-timing-function:cubic-bezier(.4,0,.2,1);transform:rotate(1deg)}to{transform:rotate(0)}}@keyframes fa-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes fa-spin-snap{0%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:rotate(0)}12%{animation-timing-function:cubic-bezier(.8,0,1,1);transform:rotate(60deg)}16.67%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:rotate(60deg)}28.67%{animation-timing-function:cubic-bezier(.8,0,1,1);transform:rotate(120deg)}33.33%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:rotate(120deg)}45.33%{animation-timing-function:cubic-bezier(.8,0,1,1);transform:rotate(180deg)}50%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:rotate(180deg)}62%{animation-timing-function:cubic-bezier(.8,0,1,1);transform:rotate(240deg)}66.67%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:rotate(240deg)}78.67%{animation-timing-function:cubic-bezier(.8,0,1,1);transform:rotate(300deg)}83.33%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:rotate(300deg)}95.33%{animation-timing-function:cubic-bezier(.8,0,1,1);transform:rotate(360deg)}to{transform:rotate(360deg)}}@keyframes fa-spin-snap-4{0%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:rotate(0)}15%{animation-timing-function:cubic-bezier(.8,0,1,1);transform:rotate(90deg)}25%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:rotate(90deg)}40%{animation-timing-function:cubic-bezier(.8,0,1,1);transform:rotate(180deg)}50%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:rotate(180deg)}65%{animation-timing-function:cubic-bezier(.8,0,1,1);transform:rotate(270deg)}75%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:rotate(270deg)}90%{animation-timing-function:cubic-bezier(.8,0,1,1);transform:rotate(360deg)}to{transform:rotate(360deg)}}@keyframes fa-spin-snap-8{0%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:rotate(0)}9%{animation-timing-function:cubic-bezier(.8,0,1,1);transform:rotate(45deg)}12.5%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:rotate(45deg)}21.5%{animation-timing-function:cubic-bezier(.8,0,1,1);transform:rotate(90deg)}25%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:rotate(90deg)}34%{animation-timing-function:cubic-bezier(.8,0,1,1);transform:rotate(135deg)}37.5%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:rotate(135deg)}46.5%{animation-timing-function:cubic-bezier(.8,0,1,1);transform:rotate(180deg)}50%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:rotate(180deg)}59%{animation-timing-function:cubic-bezier(.8,0,1,1);transform:rotate(225deg)}62.5%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:rotate(225deg)}71.5%{animation-timing-function:cubic-bezier(.8,0,1,1);transform:rotate(270deg)}75%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:rotate(270deg)}84%{animation-timing-function:cubic-bezier(.8,0,1,1);transform:rotate(315deg)}87.5%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:rotate(315deg)}96.5%{animation-timing-function:cubic-bezier(.8,0,1,1);transform:rotate(360deg)}to{transform:rotate(360deg)}}@keyframes fa-buzz{0%{animation-timing-function:cubic-bezier(.1,0,.9,1);transform:translate(0)rotate(0)}5%{transform:translateX(var(--fa-buzz-distance,4px)) rotate(.5deg)}10%{transform:translateX(calc(-1 * var(--fa-buzz-distance,4px))) rotate(-.5deg)}15%{transform:translateX(var(--fa-buzz-distance,4px)) rotate(.3deg)}20%{transform:translateX(calc(-1 * var(--fa-buzz-distance,4px))) rotate(-.3deg)}25%{transform:translateX(calc(var(--fa-buzz-distance,4px) * .7)) rotate(.2deg)}30%{transform:translateX(calc(-1 * var(--fa-buzz-distance,4px) * .7)) rotate(-.2deg)}35%{transform:translateX(calc(var(--fa-buzz-distance,4px) * .4)) rotate(.1deg)}40%{transform:translate(0)rotate(0)}to{transform:translate(0)rotate(0)}}@keyframes fa-wag{0%{animation-timing-function:cubic-bezier(.2,0,.6,1);transform:rotate(0)}12%{transform:rotate(var(--fa-wag-angle,12deg));animation-timing-function:cubic-bezier(.4,0,.2,1)}24%{animation-timing-function:cubic-bezier(.2,0,.6,1);transform:rotate(2deg)}36%{transform:rotate(calc(var(--fa-wag-angle,12deg) * .85));animation-timing-function:cubic-bezier(.4,0,.2,1)}48%{animation-timing-function:cubic-bezier(.2,0,.6,1);transform:rotate(1deg)}58%{transform:rotate(calc(var(--fa-wag-angle,12deg) * .6));animation-timing-function:cubic-bezier(.4,0,.2,1)}68%{transform:rotate(0)}to{transform:rotate(0)}}@keyframes fa-float{0%{transform:translateY(0) translateX(0) rotate(0deg) scale(var(--fa-float-squash-x,1.02), var(--fa-float-squash-y,.98));animation-timing-function:cubic-bezier(.33,0,.66,.33)}15%{transform:translateY(calc(-.4 * var(--fa-float-height,6px))) translateX(var(--fa-float-drift,1px)) rotate(var(--fa-float-tilt,1deg)) scale(1, 1);animation-timing-function:cubic-bezier(.33,.66,.66,1)}35%{transform:translateY(calc(-1 * var(--fa-float-height,6px))) translateX(0) rotate(0deg) scale(var(--fa-float-stretch-x,.98), var(--fa-float-stretch-y,1.03));animation-timing-function:cubic-bezier(.5,0,.5,0)}50%{transform:translateY(calc(-.92 * var(--fa-float-height,6px))) translateX(calc(-.5 * var(--fa-float-drift,1px))) rotate(calc(-.5 * var(--fa-float-tilt,1deg))) scale(.995, 1.01);animation-timing-function:cubic-bezier(.33,0,.66,.33)}70%{transform:translateY(calc(-.3 * var(--fa-float-height,6px))) translateX(calc(-1 * var(--fa-float-drift,1px))) rotate(calc(-1 * var(--fa-float-tilt,1deg))) scale(1, 1);animation-timing-function:cubic-bezier(.33,.66,.66,1)}90%{transform:translateY(calc(.05 * var(--fa-float-height,6px))) translateX(0) rotate(0deg) scale(var(--fa-float-squash-x,1.02), var(--fa-float-squash-y,.98));animation-timing-function:cubic-bezier(.33,0,.66,1)}to{transform:translateY(0) translateX(0) rotate(0deg) scale(var(--fa-float-squash-x,1.02), var(--fa-float-squash-y,.98))}}@keyframes fa-swing{0%{animation-timing-function:cubic-bezier(.2,0,.8,1);transform:rotate(0)}8%{transform:rotate(var(--fa-swing-angle,22deg));animation-timing-function:cubic-bezier(.3,0,.7,1)}18%{transform:rotate(calc(-1 * var(--fa-swing-angle,22deg) * .85));animation-timing-function:cubic-bezier(.3,0,.7,1)}28%{transform:rotate(calc(var(--fa-swing-angle,22deg) * .65));animation-timing-function:cubic-bezier(.35,0,.65,1)}38%{transform:rotate(calc(-1 * var(--fa-swing-angle,22deg) * .45));animation-timing-function:cubic-bezier(.4,0,.6,1)}48%{transform:rotate(calc(var(--fa-swing-angle,22deg) * .25));animation-timing-function:cubic-bezier(.4,0,.6,1)}56%{transform:rotate(calc(-1 * var(--fa-swing-angle,22deg) * .1));animation-timing-function:cubic-bezier(.4,0,.6,1)}64%{transform:rotate(0)}to{transform:rotate(0)}}@keyframes fa-jello{0%{animation-timing-function:cubic-bezier(.2,0,.8,1);transform:scale(1)}12%{transform:scale(var(--fa-jello-scale-x,1.15), calc(2 - var(--fa-jello-scale-x,1.15)));animation-timing-function:cubic-bezier(.3,0,.7,1)}24%{transform:scale(calc(2 - var(--fa-jello-scale-y,1.12)), var(--fa-jello-scale-y,1.12));animation-timing-function:cubic-bezier(.3,0,.7,1)}36%{transform:scale(calc(1 + (var(--fa-jello-scale-x,1.15) - 1) * .5), calc(2 - (1 + (var(--fa-jello-scale-x,1.15) - 1) * .5)));animation-timing-function:cubic-bezier(.4,0,.6,1)}48%{transform:scale(calc(2 - (1 + (var(--fa-jello-scale-y,1.12) - 1) * .3)), calc(1 + (var(--fa-jello-scale-y,1.12) - 1) * .3));animation-timing-function:cubic-bezier(.4,0,.6,1)}58%{animation-timing-function:cubic-bezier(.4,0,.2,1);transform:scale(1.02,.98)}68%{transform:scale(1)}to{transform:scale(1)}}.fa-rotate-90{transform:rotate(90deg)}.fa-rotate-180{transform:rotate(180deg)}.fa-rotate-270{transform:rotate(270deg)}.fa-flip-horizontal{transform:scaleX(-1)}.fa-flip-vertical{transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{transform:scale(-1)}.fa-rotate-by{transform:rotate(var(--fa-rotate-angle,0))}.svg-inline--fa .fa-primary{fill:var(--fa-primary-color,currentColor);opacity:var(--fa-primary-opacity,1)}.svg-inline--fa .fa-secondary{fill:var(--fa-secondary-color,currentColor);opacity:var(--fa-secondary-opacity,.4)}.svg-inline--fa.fa-swap-opacity .fa-primary{opacity:var(--fa-secondary-opacity,.4)}.svg-inline--fa.fa-swap-opacity .fa-secondary{opacity:var(--fa-primary-opacity,1)}.svg-inline--fa mask .fa-primary,.svg-inline--fa mask .fa-secondary{fill:#000}.svg-inline--fa.fa-inverse{fill:var(--fa-inverse,#fff)}.fa-stack{vertical-align:middle;width:2.5em;height:2em;line-height:2em;display:inline-block;position:relative}.fa-inverse{color:var(--fa-inverse,#fff)}.svg-inline--fa.fa-stack-1x{--fa-width:1.25em;height:1em;width:var(--fa-width)}.svg-inline--fa.fa-stack-2x{--fa-width:2.5em;height:2em;width:var(--fa-width)}.fa-stack-1x,.fa-stack-2x{z-index:var(--fa-stack-z-index,auto);margin:auto;position:absolute;inset:0}body{font-family:Arial,Liberation Sans,DejaVu Sans,sans-serif;font-size:12px}body.mobile,body.mobile .message{font-size:14px}pre{font-family:Consolas,Monaco,Ubuntu Mono,monospace}table.env-table tbody tr td{vertical-align:top;border-top:none;height:18px;line-height:18px}table.env-table,table.env-table table{border-spacing:0;border-collapse:collapse}table.env-table td{padding-right:5px}tbody tr{width:98%}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.loading{border:4px solid #f3f3f3;border-top-color:#3498db;border-radius:50%;width:30px;height:30px;margin:40dvh auto;animation:1s linear infinite spin}.message-row-wrapper{width:inherit;display:flex}.message-row-wrapper .grouping-checkbox{margin:0}.message-row{width:inherit;display:flex}.message-row .severity,.message-row .protected{text-align:center;flex-grow:0;flex-shrink:0;width:25px;font-size:12px}.message-row div{border-top:.5px solid #e9e9e9;padding-top:1px;padding-bottom:1px;line-height:25px}.message-row .count{box-sizing:border-box;flex-grow:0;flex-shrink:0;width:30px;padding-right:4px;font-size:11px;font-weight:700}.message-row .message-body{white-space:nowrap;text-overflow:ellipsis;flex-grow:1;flex-shrink:1;font-size:13px;overflow:hidden}.message-row .time{color:#999;vertical-align:top;flex-grow:0;flex-shrink:0;padding-right:8px;font-size:12px}.message-row .count{text-align:right}.message-row:hover{cursor:pointer;background-color:#f8f8f8}.message-row.selected{background-color:#dfdfdf}.svg-inline--fa.fatal{color:#e00}.svg-inline--fa.error{color:#900}.svg-inline--fa.warning{color:#feb800}.debug{color:#777}.action-panel .search{vertical-align:middle;box-sizing:border-box;border:1px solid #ddd;padding:3px}#log-table .show-more{text-align:center;cursor:pointer;background-color:#ddd;height:30px;margin-top:8px;line-height:30px;text-decoration:none}#bottom-panel{z-index:2;background-color:#f1f1f1;height:300px;padding:0 8px 8px;position:fixed;bottom:0;left:0;right:0}#bottom-panel.full{background-color:inherit;height:90%;position:static}#bottom-panel.full>div{padding-bottom:40px}#bottom-panel.full .tabs{display:none}#bottom-panel.full .message-info{position:static}#bottom-panel.full .message-info .content{display:block;position:static}#bottom-panel.full .save,#bottom-panel.full .share{bottom:10px}#bottom-panel.full button.delete{display:none}#bottom-panel.full .message-actions button{margin-top:8px}@media (width<=382px){#bottom-panel.full .message-actions{height:73px}}@media (width>=383px){#bottom-panel.full .message-actions{height:40px}}#bottom-panel.full .message-actions{background-color:#eee;border-top:1px solid #dfdfdf;width:100%;padding-left:10px;position:fixed;bottom:0;left:0}.message-actions{margin-right:10px;position:absolute;bottom:5px;right:0}.message-actions button{margin-left:5px}.message-actions button.copied{color:#176b3a;background:#d5f5e3}.message-actions button.copy-failed{color:#9b1c1c;background:#fde2e2}.divider{cursor:row-resize;background-color:#fafafa;border-top:1px solid #ddd;border-bottom:1px solid #ddd;height:15px;position:fixed;bottom:310px;left:0;right:0}.divider div{background-color:#ccc;width:24px;height:1px;margin:auto;position:relative}.divider .line-1{top:5px}.divider .line-2{top:6px}.divider .line-3{top:7px}#top-panel{position:fixed;inset:0 0 320px;overflow:auto}#top-panel.with-top-menu{top:35px}#top-menu{z-index:900;width:100%;position:fixed;top:0;left:0}#back-to-site-panel{background-color:#f1f1f1;height:15px;padding:10px}.message-info{border-bottom:1px solid #ddd;position:absolute;top:0;left:0;right:0}#bottom-panel.group-view .message-info{top:43px}.nav-controls.group-nav{border-bottom:1px solid #ddd;position:absolute;left:0;right:0}.action-panel{font-weight:700;position:absolute;bottom:0;left:0;right:0}.action-panel input{margin:0}.action-panel .svg-inline--fa,input,label span{vertical-align:middle}.severity-filters label{margin-right:10px}.search-clear-all .clear{float:right;vertical-align:middle}#log-table{width:99%;margin:auto}.btn span{display:inline}.hidden{display:none}.message-info pre,.message-info .env-table{margin:5px 10px 10px;position:relative}#overlay{z-index:99999;cursor:row-resize;opacity:0;position:fixed;inset:0}.message-info .content{display:none;position:absolute;inset:0 0 35px;overflow:auto}.message-info .content.active{display:block}.tabs{border-top:1px solid #ddd;margin:0 0 5px;padding:0 0 0 10px;list-style-type:none;position:absolute;bottom:13px;left:0;right:0}.tabs li{float:left;margin:0;padding-right:5px;position:relative}.tabs a{color:#333;background-color:#e1e1e1;border:1px solid #ddd;border-top:none;border-bottom-right-radius:5px;border-bottom-left-radius:5px;padding:6px;text-decoration:none;position:relative;top:6px}.tabs a.active{background-color:#f1f1f1;border-top:1px solid #f1f1f1}.btn{text-align:center;cursor:pointer;color:#333;background-color:#ddd;border:none;margin:0;padding:5px 8px;font-size:1em;font-weight:400;line-height:0;text-decoration:none;transition:all .25s;display:inline-block}.btn:hover{color:#000;background-color:#ccc}.btn .svg-inline--fa{margin-right:7px}.btn:active{text-shadow:none}.btn.danger:hover{color:#eee;background-color:#c63c1b}.btn.ok{color:#fff;background-color:#3781dc}.btn.ok:hover{color:#fff;background-color:#286dc2}.search-clear-all .search,.search-clear-all .btn.clear{height:100%}.search-clear-all .btn.clear span{vertical-align:middle}.severity-filters label,.search-clear-all .search,.search-clear-all .btn.clear{align-self:center}.search-clear-all .footer-btns .settings{box-sizing:border-box;align-items:center;height:100%;display:inline-flex}.search-clear-all{justify-content:space-between;display:flex}.search-clear-all .search{flex-shrink:2;min-width:0}.footer-btns{gap:.7em;display:flex}@media (width>=871px){.severity-filters,.search-clear-all{height:100%}.severity-filters,.more-wrapping{display:flex}.severity-filters{float:left}.action-panel{box-sizing:border-box;height:45px;padding:10px}.message-info{bottom:45px}}@media (width<=870px){.severity-filters{padding:10px}.search-clear-all{padding:0 10px 10px}.action-panel{height:69px}.message-info{bottom:69px}}@media (width<=430px){.severity-filters{white-space:nowrap;overflow:scroll hidden}.more-wrapping:before,.more-wrapping:after{content:"";height:18px;position:absolute}.more-wrapping:before{background:linear-gradient(90deg,#f1f1f1 0%,#f1f1f100 100%);width:15px;margin-left:-10px}.more-wrapping:after{background:linear-gradient(270deg,#f1f1f1 0%,#f1f1f100 100%);width:23px;right:0}}.btn.no-text .svg-inline--fa{margin:0}.btn[disabled]{opacity:.5}.actions-menu{z-index:3;background:#fafafa;flex-direction:column;width:115px;padding:5px 5px 0;display:inline-flex;position:absolute;bottom:27px;right:45px;box-shadow:0 4px 14px #00000026}.actions-menu button{height:27px;margin:0 0 5px}.nav-controls{padding:10px}#bottom-panel:not(.full) .nav-controls.env-nav{position:-moz-sticky;position:-ms-sticky;position:-o-sticky;z-index:1;background:#f1f1f1;border-bottom:1px solid #ddd;position:sticky;top:0}.current-number{margin:0 7px}.recent-link,.expand-list{color:#00f;cursor:pointer;text-decoration:underline}.settings-page{max-width:1110px;margin-left:auto;margin-right:auto;padding:0 10px 70px;font-size:15px}.settings-header{align-items:center;margin-bottom:5px;display:flex}.settings-header .header-title{flex-grow:1}.settings-header .header-logo{width:50px;height:50px}.settings-section{padding-top:15px}.settings-section .section-title{margin-top:0}.settings-section .subsection-title{margin-bottom:10px}.settings-section .tip{color:gray;font-size:13px;font-style:italic}.settings-section .pattern-wrapper{height:33px;margin-top:10px;font-size:16px;display:flex}.settings-section .api-error{color:red;margin-top:5px;font-family:Consolas,Monaco,Ubuntu Mono,monospace}.pattern-wrapper .pattern-input{width:600px;font-family:Consolas,Monaco,Ubuntu Mono,monospace;font-size:inherit;box-sizing:border-box;vertical-align:middle;flex-grow:1;flex-shrink:1;height:100%;padding:5px 8px}.retro-checkbox .checkbox{margin:0}.retro-checkbox{margin-top:7px}.btn.new-pattern{height:100%;line-height:18px}.pattern-wrapper .shrink{text-align:center;box-sizing:border-box;vertical-align:middle;flex-grow:0;flex-shrink:0;width:40px;height:100%;margin-left:10px}.svg-inline--fa{opacity:.7}.pattern-wrapper .shrink.reset{background:unset;width:unset;margin-left:8px;padding:0}.grouping-patterns button.new-pattern{margin-top:10px}.grouping-pattern-dialog-overlay{z-index:100000;background:#00000080;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.grouping-pattern-dialog{background:#fff;border-radius:4px;min-width:600px;padding:20px;box-shadow:0 4px 16px #0000004d}.grouping-pattern-dialog h3{margin:0 0 8px;font-size:1em;font-weight:700}.grouping-pattern-dialog p{color:#555;margin:0 0 10px;font-size:.9em}.grouping-pattern-input{box-sizing:border-box;border:1px solid #ccc;border-radius:3px;width:100%;margin-bottom:14px;padding:8px;font-family:monospace;font-size:1.1em}.grouping-pattern-dialog .api-error{color:#c62828;white-space:pre-wrap;margin:0 0 14px}.grouping-pattern-dialog-buttons{justify-content:flex-end;gap:8px;display:flex}.grouping-pattern-dialog .btn{padding:12px}.backtrace-line .line-link{color:#9e9e9e;white-space:normal;margin-left:3px;text-decoration:none}@media (prefers-color-scheme:dark){body{color:#cecece;background:#181818}#bottom-panel{background:#181818}.message-row div{border-top-color:#404040}.message-row:hover{background:#262626}.message-row.selected{background:#303030}#log-table .show-more,.divider{background:#262626}.divider div{background:#707070}.divider,.tabs{border-top-color:#555}#bottom-panel:not(.full) .nav-controls.env-nav,.divider,.message-info,.nav-controls.group-nav{border-bottom-color:#555}.action-panel .search{background:#262626;border-color:#404040}.btn,.tabs a{color:#cecece}.tabs a{background:#323232;border-color:#404040}.tabs a.active{color:#f4f4f4;background:#555;border-color:#707070}.btn{background:#323232}#bottom-panel.full .message-actions{background:#181818;border-color:#555}#bottom-panel:not(.full) .nav-controls.env-nav{background:#181818}.recent-link,.expand-list{color:#44f}#back-to-site-panel{background-color:#181818}.grouping-pattern-dialog{color:#cecece;background:#262626}.grouping-pattern-dialog p{color:#aaa}.grouping-pattern-input{color:#cecece;background:#181818;border-color:#555}}
@@ -5,6 +5,7 @@ module Logster
5
5
  attr_accessor(
6
6
  :allow_grouping,
7
7
  :application_version,
8
+ :authorize_request,
8
9
  :current_context,
9
10
  :env_expandable_keys,
10
11
  :enable_custom_patterns_via_ui,
@@ -5,6 +5,12 @@ module Logster
5
5
  class Reporter
6
6
  PATH_INFO = "PATH_INFO"
7
7
  SCRIPT_NAME = "SCRIPT_NAME"
8
+ REQUEST_METHOD = "REQUEST_METHOD"
9
+ SECURITY_HEADERS = {
10
+ "cache-control" => "no-store",
11
+ "referrer-policy" => "no-referrer",
12
+ "x-content-type-options" => "nosniff",
13
+ }.freeze
8
14
 
9
15
  def initialize(app, config = {})
10
16
  @app = app
@@ -20,7 +26,11 @@ module Logster
20
26
  path = script_name + path if script_name && script_name.length > 0
21
27
 
22
28
  if path == @error_path
23
- return 403, {}, ["Access Denied"] if !Logster.config.enable_js_error_reporting
29
+ if env[REQUEST_METHOD] != "POST"
30
+ return response(405, "Method not allowed", "allow" => "POST")
31
+ end
32
+ return response(403, "CSRF validation failed") unless valid_csrf_request?(env)
33
+ return response(403, "Access Denied") if !Logster.config.enable_js_error_reporting
24
34
 
25
35
  Logster
26
36
  .config
@@ -29,12 +39,12 @@ module Logster
29
39
  if Logster.config.rate_limit_error_reporting
30
40
  req = Rack::Request.new(env)
31
41
  if Logster.store.rate_limited?(req.ip, perform: true)
32
- return 429, {}, ["Rate Limited"]
42
+ return response(429, "Rate Limited")
33
43
  end
34
44
  end
35
45
  report_js_error(env)
36
46
  end
37
- return 200, {}, ["OK"]
47
+ return response(200, "OK")
38
48
  end
39
49
 
40
50
  @app.call(env)
@@ -42,6 +52,27 @@ module Logster
42
52
  Thread.current[Logster::Logger::LOGSTER_ENV] = nil
43
53
  end
44
54
 
55
+ def response(status, body, headers = {})
56
+ [
57
+ status,
58
+ SECURITY_HEADERS.merge("content-type" => "text/plain; charset=utf-8").merge(headers),
59
+ [body],
60
+ ]
61
+ end
62
+
63
+ def valid_csrf_request?(env)
64
+ fetch_site = env["HTTP_SEC_FETCH_SITE"]
65
+ return false if fetch_site && !%w[same-origin same-site none].include?(fetch_site)
66
+
67
+ same_origin_fetch = fetch_site == "same-origin"
68
+ ajax_request = env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest"
69
+ return false unless same_origin_fetch || ajax_request
70
+ return true if same_origin_fetch
71
+
72
+ origin = env["HTTP_ORIGIN"]
73
+ !origin || origin == Rack::Request.new(env).base_url
74
+ end
75
+
45
76
  def report_js_error(env)
46
77
  req = Rack::Request.new(env)
47
78
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "json"
4
+ require "uri"
4
5
 
5
6
  module Logster
6
7
  module Middleware
@@ -8,6 +9,39 @@ module Logster
8
9
  PATH_INFO = "PATH_INFO".freeze
9
10
  SCRIPT_NAME = "SCRIPT_NAME".freeze
10
11
  REQUEST_METHOD = "REQUEST_METHOD".freeze
12
+ ASSET_NAME = /\A[a-zA-Z0-9._-]+\z/
13
+ LEGACY_SCRIPTS = %w[vendor.js].freeze
14
+ LOGSTER_RESPONSE = "logster.response".freeze
15
+ STATIC_RESPONSE = "logster.static_response".freeze
16
+ DYNAMIC_SECURITY_HEADERS = {
17
+ "cache-control" => "no-store",
18
+ "referrer-policy" => "no-referrer",
19
+ "x-content-type-options" => "nosniff",
20
+ "x-frame-options" => "DENY",
21
+ }.freeze
22
+ STATIC_SECURITY_HEADERS = {
23
+ "cache-control" => "public, max-age=0, must-revalidate",
24
+ "referrer-policy" => "no-referrer",
25
+ "x-content-type-options" => "nosniff",
26
+ }.freeze
27
+ MESSAGE_ROUTE = %r{\A/message/([0-9a-f]+)\z}
28
+ PROTECT_ROUTE = %r{\A/protect/([0-9a-f]+)\z}
29
+ UNPROTECT_ROUTE = %r{\A/unprotect/([0-9a-f]+)\z}
30
+ SOLVE_ROUTE = %r{\A/solve/([0-9a-f]+)\z}
31
+ CLEAR_ROUTE = %r{\A/clear\z}
32
+ PATTERNS_ROUTE = %r{\A/patterns/([a-zA-Z0-9_]+)\.json\z}
33
+ RESET_COUNT_ROUTE = %r{\A/reset-count\.json\z}
34
+ SOLVE_GROUP_ROUTE = %r{\A/solve-group\z}
35
+ MUTATING_ROUTES = [
36
+ [MESSAGE_ROUTE, %w[DELETE]],
37
+ [PROTECT_ROUTE, %w[PUT]],
38
+ [UNPROTECT_ROUTE, %w[DELETE]],
39
+ [SOLVE_ROUTE, %w[PUT]],
40
+ [CLEAR_ROUTE, %w[POST]],
41
+ [PATTERNS_ROUTE, %w[POST PUT DELETE]],
42
+ [RESET_COUNT_ROUTE, %w[PUT]],
43
+ [SOLVE_GROUP_ROUTE, %w[POST]],
44
+ ].freeze
11
45
 
12
46
  def initialize(app)
13
47
  @app = app
@@ -21,60 +55,86 @@ module Logster
21
55
  end
22
56
 
23
57
  def call(env)
58
+ env.delete(LOGSTER_RESPONSE)
59
+ env.delete(STATIC_RESPONSE)
60
+ response = dispatch(env)
61
+ return response unless env.delete(LOGSTER_RESPONSE)
62
+
63
+ status, headers, body = response
64
+ security_headers =
65
+ env.delete(STATIC_RESPONSE) ? STATIC_SECURITY_HEADERS : DYNAMIC_SECURITY_HEADERS
66
+ [status, security_headers.merge(headers), body]
67
+ end
68
+
69
+ def dispatch(env)
24
70
  path = env[PATH_INFO]
25
71
  script_name = env[SCRIPT_NAME]
26
72
 
27
73
  path = script_name + path if script_name && script_name.length > 0
28
74
 
29
75
  if resource = resolve_path(path)
30
- if resource =~
31
- /\.ico$|\.js$|\.png|\.handlebars$|\.css$|\.woff$|\.ttf$|\.woff2$|\.svg$|\.otf$|\.eot$/
76
+ env[LOGSTER_RESPONSE] = true
77
+
78
+ if (authorize_request = Logster.config.authorize_request) && !authorize_request.call(env)
79
+ return not_allowed("Not authorized")
80
+ end
81
+
82
+ return not_found if parent_directory_segment?(resource)
83
+
84
+ if allowed_methods = allowed_methods_for(resource)
85
+ if !allowed_methods.include?(env[REQUEST_METHOD])
86
+ return method_not_allowed(allowed_methods)
87
+ end
88
+ return not_allowed("CSRF validation failed") unless valid_csrf_request?(env)
89
+ end
90
+
91
+ if resource =~ %r{\A/(?:.*\.(?:ico|js|png|handlebars|css|woff|ttf|woff2|svg|otf|eot))\z}
32
92
  serve_file(env, resource)
33
- elsif resource.start_with?("/messages.json") && env[REQUEST_METHOD] == "POST"
93
+ elsif resource == "/messages.json" && env[REQUEST_METHOD] == "POST"
34
94
  serve_messages(Rack::Request.new(env))
35
- elsif resource =~ %r{/message/([0-9a-f]+)$}
95
+ elsif (route_match = MESSAGE_ROUTE.match(resource))
36
96
  return method_not_allowed("DELETE") if env[REQUEST_METHOD] != "DELETE"
37
97
 
38
- key = $1
98
+ key = route_match[1]
39
99
  message = Logster.store.get(key)
40
100
  return 404, {}, ["Message not found"] unless message
41
101
 
42
102
  Logster.store.delete(message)
43
- [301, { "Location" => "#{@logs_path}/" }, []]
44
- elsif resource =~ %r{/(un)?protect/([0-9a-f]+)$}
45
- off = $1 == "un"
46
- key = $2
103
+ [303, { "location" => "#{@logs_path}/" }, []]
104
+ elsif (route_match = PROTECT_ROUTE.match(resource) || UNPROTECT_ROUTE.match(resource))
105
+ off = resource.start_with?("/unprotect/")
106
+ key = route_match[1]
47
107
 
48
108
  message = Logster.store.get(key)
49
109
  return 404, {}, ["Message not found"] unless message
50
110
 
51
111
  if off
52
112
  if Logster.store.unprotect(key)
53
- [301, { "Location" => "#{@logs_path}/show/#{key}?protected=false" }, []]
113
+ [303, { "location" => "#{@logs_path}/show/#{key}?protected=false" }, []]
54
114
  else
55
115
  [500, {}, ["Failed"]]
56
116
  end
57
117
  else
58
118
  if Logster.store.protect(key)
59
- [301, { "Location" => "#{@logs_path}/show/#{key}?protected=true" }, []]
119
+ [303, { "location" => "#{@logs_path}/show/#{key}?protected=true" }, []]
60
120
  else
61
121
  [500, {}, ["Failed"]]
62
122
  end
63
123
  end
64
- elsif resource =~ %r{/solve/([0-9a-f]+)$}
65
- key = $1
124
+ elsif (route_match = SOLVE_ROUTE.match(resource))
125
+ key = route_match[1]
66
126
 
67
127
  message = Logster.store.get(key)
68
128
  return 404, {}, ["Message not found"] unless message
69
129
 
70
130
  Logster.store.solve(key)
71
131
 
72
- [301, { "Location" => "#{@logs_path}" }, []]
73
- elsif resource =~ %r{/clear$}
132
+ [303, { "location" => "#{@logs_path}" }, []]
133
+ elsif CLEAR_ROUTE.match?(resource)
74
134
  return method_not_allowed("POST") if env[REQUEST_METHOD] != "POST"
75
135
  Logster.store.clear
76
136
  [200, {}, ["Messages cleared"]]
77
- elsif resource =~ %r{/show/([0-9a-f]+)(\.json)?$}
137
+ elsif resource =~ %r{\A/show/([0-9a-f]+)(\.json)?\z}
78
138
  key = $1
79
139
  json = $2 == ".json"
80
140
 
@@ -87,7 +147,7 @@ module Logster
87
147
  preload = { "/show/#{key}" => message }
88
148
  js_app(preload)
89
149
  end
90
- elsif resource =~ %r{/settings(\.json)?$}
150
+ elsif resource =~ %r{\A/settings(\.json)?\z}
91
151
  json = $1 == ".json"
92
152
  if json
93
153
  ignore_count = Logster.store.get_all_ignore_count
@@ -116,7 +176,7 @@ module Logster
116
176
  else
117
177
  js_app
118
178
  end
119
- elsif resource =~ %r{/patterns/([a-zA-Z0-9_]+)\.json$}
179
+ elsif (route_match = PATTERNS_ROUTE.match(resource))
120
180
  unless Logster.config.enable_custom_patterns_via_ui
121
181
  return(
122
182
  not_allowed(
@@ -125,12 +185,12 @@ module Logster
125
185
  )
126
186
  end
127
187
 
128
- set_name = $1
188
+ set_name = route_match[1]
129
189
  req = Rack::Request.new(env)
130
190
  return method_not_allowed(%w[POST PUT DELETE]) if req.request_method == "GET"
131
191
 
132
192
  update_patterns(set_name, req)
133
- elsif resource == "/reset-count.json"
193
+ elsif RESET_COUNT_ROUTE.match?(resource)
134
194
  req = Rack::Request.new(env)
135
195
  return method_not_allowed("PUT") if req.request_method != "PUT"
136
196
  pattern = nil
@@ -152,7 +212,7 @@ module Logster
152
212
  [200, {}, ["OK"]]
153
213
  elsif resource == "/"
154
214
  js_app
155
- elsif resource =~ %r{/fetch-env/([0-9a-f]+)\.json$}
215
+ elsif resource =~ %r{\A/fetch-env/([0-9a-f]+)\.json\z}
156
216
  key = $1
157
217
  env = Logster.store.get_env(key)
158
218
  if env
@@ -160,7 +220,7 @@ module Logster
160
220
  else
161
221
  not_found
162
222
  end
163
- elsif resource == "/solve-group"
223
+ elsif SOLVE_GROUP_ROUTE.match?(resource)
164
224
  return not_allowed unless Logster.config.enable_custom_patterns_via_ui
165
225
  req = Rack::Request.new(env)
166
226
  return method_not_allowed("POST") if req.request_method != "POST"
@@ -185,7 +245,30 @@ module Logster
185
245
 
186
246
  protected
187
247
 
248
+ def parent_directory_segment?(resource)
249
+ Rack::Utils.unescape_path(resource).split("/").include?("..")
250
+ rescue ArgumentError
251
+ true
252
+ end
253
+
254
+ def allowed_methods_for(resource)
255
+ route = MUTATING_ROUTES.find { |pattern, _| pattern.match?(resource) }
256
+ route&.last
257
+ end
258
+
259
+ def valid_csrf_request?(env)
260
+ return false unless env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest"
261
+
262
+ fetch_site = env["HTTP_SEC_FETCH_SITE"]
263
+ return false if fetch_site && !%w[same-origin same-site none].include?(fetch_site)
264
+ return true if fetch_site == "same-origin"
265
+
266
+ origin = env["HTTP_ORIGIN"]
267
+ !origin || origin == Rack::Request.new(env).base_url
268
+ end
269
+
188
270
  def serve_file(env, path)
271
+ env[STATIC_RESPONSE] = true
189
272
  env[PATH_INFO] = path
190
273
  # accl redirect is going to be trouble, ensure its bypassed
191
274
  env["sendfile.type"] = ""
@@ -307,6 +390,93 @@ module Logster
307
390
  "<script src='#{@logs_path}/javascript/#{name}' nonce='#{csp_nonce}'></script>"
308
391
  end
309
392
 
393
+ def module_script(name, csp_nonce)
394
+ "<script type='module' src='#{@logs_path}/javascript/#{name}' nonce='#{csp_nonce}'></script>"
395
+ end
396
+
397
+ def modulepreload(name, csp_nonce)
398
+ "<link rel='modulepreload' href='#{@logs_path}/javascript/#{name}' nonce='#{csp_nonce}'>"
399
+ end
400
+
401
+ # Vite's own build manifest, plus the application config the build emits
402
+ # alongside it.
403
+ def asset_manifest
404
+ @asset_manifest ||=
405
+ begin
406
+ vite = JSON.parse(read_asset("manifest.json"))
407
+ entry = vite.values.find { |chunk| chunk.is_a?(Hash) && chunk["isEntry"] }
408
+ raise KeyError, "the Vite build emitted no entry chunk" unless entry
409
+
410
+ validate_asset_manifest(
411
+ "scripts" => plain_scripts,
412
+ "module" => asset_basename(entry.fetch("file")),
413
+ "modulepreload" => preloaded_chunks(vite, entry),
414
+ "stylesheets" => Array(entry["css"]).map { |href| asset_basename(href) },
415
+ "config" => JSON.parse(read_asset("logster-config.json")),
416
+ )
417
+ rescue JSON::ParserError, KeyError, TypeError, Errno::ENOENT => error
418
+ raise RuntimeError, "Logster asset manifest is invalid: #{error.message}"
419
+ end
420
+ end
421
+
422
+ def read_asset(name)
423
+ path = File.join(@assets_path, name)
424
+ unless File.file?(path)
425
+ raise RuntimeError,
426
+ "Logster asset manifest is missing; rebuild the gem with ./build_client_app.sh"
427
+ end
428
+ File.read(path)
429
+ end
430
+
431
+ def asset_basename(path)
432
+ File.basename(path.to_s)
433
+ end
434
+
435
+ # classicEmberSupport emits this to set EmberENV before the deferred
436
+ # module entry runs. It is absent once the app no longer needs the shim.
437
+ def plain_scripts
438
+ LEGACY_SCRIPTS.select { |name| File.file?(File.join(@assets_path, "javascript", name)) }
439
+ end
440
+
441
+ # Every chunk the entry pulls in eagerly, once each, in discovery order.
442
+ def preloaded_chunks(vite, chunk, names = [], seen = {})
443
+ Array(chunk["imports"]).each do |key|
444
+ next if seen[key]
445
+ seen[key] = true
446
+
447
+ imported = vite[key]
448
+ raise KeyError, "the Vite manifest is missing chunk #{key}" unless imported
449
+
450
+ names << asset_basename(imported.fetch("file"))
451
+ preloaded_chunks(vite, imported, names, seen)
452
+ end
453
+ names
454
+ end
455
+
456
+ def validate_asset_manifest(manifest)
457
+ %w[modulepreload stylesheets].each do |type|
458
+ names = manifest.fetch(type)
459
+ unless names.is_a?(Array) && names.all? { |name| ASSET_NAME.match?(name.to_s) }
460
+ raise JSON::ParserError, "Invalid #{type} asset manifest"
461
+ end
462
+ end
463
+ unless ASSET_NAME.match?(manifest.fetch("module").to_s)
464
+ raise JSON::ParserError, "Invalid module asset manifest"
465
+ end
466
+ unless manifest["config"].is_a?(Hash)
467
+ raise JSON::ParserError, "Invalid client application config"
468
+ end
469
+
470
+ manifest
471
+ end
472
+
473
+ def encoded_client_app_config
474
+ config = JSON.parse(JSON.generate(asset_manifest.fetch("config")))
475
+ config["environment"] = "production"
476
+ config["rootURL"] = "#{@logs_path}/"
477
+ Rack::Utils.escape_html(URI.encode_uri_component(JSON.generate(config)))
478
+ end
479
+
310
480
  def to_json_and_escape(payload)
311
481
  Rack::Utils.escape_html(JSON.generate(payload))
312
482
  end
@@ -344,8 +514,14 @@ module Logster
344
514
  def js_app(preload = {})
345
515
  csp_nonce = SecureRandom.hex
346
516
  preload = preloaded_data.merge(preload)
347
- root_url = @logs_path
348
- root_url += "/" if root_url[-1] != "/"
517
+ manifest = asset_manifest
518
+ stylesheets = manifest.fetch("stylesheets").map { |name| css(name, csp_nonce) }.join("\n")
519
+ javascript = [
520
+ # The plain scripts set up EmberENV, so they run before the module entry.
521
+ *manifest.fetch("scripts").map { |name| script(name, csp_nonce) },
522
+ *manifest.fetch("modulepreload").map { |name| modulepreload(name, csp_nonce) },
523
+ module_script(manifest.fetch("module"), csp_nonce),
524
+ ].join("\n")
349
525
  body = <<~HTML
350
526
  <!doctype html>
351
527
  <html>
@@ -353,18 +529,14 @@ module Logster
353
529
  <link rel="shortcut icon" href="#{@logs_path}/images/icon_64x64.png">
354
530
  <link rel="apple-touch-icon" href="#{@logs_path}/images/icon_144x144.png" />
355
531
  <title>#{Logster.config.web_title || "Logs"}</title>
356
- <link href='//fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type='text/css' nonce='#{csp_nonce}'>
357
- <link href='//fonts.googleapis.com/css?family=Roboto+Mono' rel='stylesheet' type='text/css' nonce='#{csp_nonce}'>
358
532
  <meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, user-scalable=yes">
359
533
  <meta name="color-scheme" content="dark light">
360
- #{css("vendor.css", csp_nonce)}
361
- #{css("client-app.css", csp_nonce)}
362
- #{script("vendor.js", csp_nonce)}
534
+ #{stylesheets}
363
535
  <meta id="preloaded-data" data-root-path="#{@logs_path}" data-preloaded="#{to_json_and_escape(preload)}">
364
- <meta name="client-app/config/environment" content="%7B%22modulePrefix%22%3A%22client-app%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22#{root_url}%22%2C%22locationType%22%3A%22history%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%7D%2C%22_APPLICATION_TEMPLATE_WRAPPER%22%3Afalse%2C%22_DEFAULT_ASYNC_OBSERVERS%22%3Atrue%2C%22_JQUERY_INTEGRATION%22%3Afalse%2C%22_TEMPLATE_ONLY_GLIMMER_COMPONENTS%22%3Atrue%7D%2C%22APP%22%3A%7B%22name%22%3A%22client-app%22%2C%22version%22%3A%220.0.0%2B7a424002%22%7D%2C%22exportApplicationGlobal%22%3Afalse%7D" />
536
+ <meta name="client-app/config/environment" content="#{encoded_client_app_config}" />
365
537
  </head>
366
538
  <body>
367
- #{script("client-app.js", csp_nonce)}
539
+ #{javascript}
368
540
  </body>
369
541
  </html>
370
542
  HTML
@@ -374,7 +546,7 @@ module Logster
374
546
  {
375
547
  "content-type" => "text/html; charset=utf-8",
376
548
  "content-security-policy" =>
377
- "script-src 'nonce-#{csp_nonce}'; style-src 'nonce-#{csp_nonce}'; object-src 'none'; base-uri 'none';",
549
+ "default-src 'none'; script-src 'nonce-#{csp_nonce}' 'strict-dynamic'; style-src 'self' 'nonce-#{csp_nonce}'; font-src 'self'; img-src 'self'; connect-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'; form-action 'self';",
378
550
  },
379
551
  [body],
380
552
  ]
@@ -25,13 +25,7 @@ module Logster::Rails
25
25
  logger.level = ::Rails.logger.level
26
26
 
27
27
  Logster.logger = config.logger = logger
28
-
29
- if rails_71?
30
- ::Rails.logger.broadcast_to(logger)
31
- else
32
- logger.chain(::Rails.logger)
33
- ::Rails.logger = logger
34
- end
28
+ ::Rails.logger.broadcast_to(logger)
35
29
  end
36
30
 
37
31
  def initialize!(app)
@@ -42,14 +36,9 @@ module Logster::Rails
42
36
  app.middleware.insert_before ActionDispatch::ShowExceptions, Logster::Middleware::Reporter
43
37
  end
44
38
 
45
- if Rails::VERSION::MAJOR == 3
46
- app.middleware.insert_before ActionDispatch::DebugExceptions,
47
- Logster::Middleware::DebugExceptions
48
- else
49
- app.middleware.insert_before ActionDispatch::DebugExceptions,
50
- Logster::Middleware::DebugExceptions,
51
- Rails.application
52
- end
39
+ app.middleware.insert_before ActionDispatch::DebugExceptions,
40
+ Logster::Middleware::DebugExceptions,
41
+ Rails.application
53
42
 
54
43
  app.middleware.delete ActionDispatch::DebugExceptions
55
44
  app.config.colorize_logging = false
@@ -63,13 +52,8 @@ module Logster::Rails
63
52
  private
64
53
 
65
54
  def logster_enabled?
66
- return ::Rails.logger == Logster.logger unless rails_71?
67
55
  ::Rails.logger.broadcasts.include?(Logster.logger)
68
56
  end
69
-
70
- def rails_71?
71
- ::Rails.version >= "7.1"
72
- end
73
57
  end
74
58
 
75
59
  class Railtie < ::Rails::Railtie
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "json"
4
- # Disable rubocop because the require is only redundant for Ruby 3.2+ but we still support Ruby 3.1
5
- require "set" # rubocop:disable Lint/RedundantRequireStatement
6
4
  require "logster/base_store"
7
5
  require "logster/redis_rate_limiter"
8
6
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Logster
4
- VERSION = "2.21.0"
4
+ VERSION = "3.0.1"
5
5
  end
data/lib/logster.rb CHANGED
@@ -57,4 +57,4 @@ end
57
57
  # check logster/configuration.rb for config options
58
58
  # Logster.config.environments << :staging
59
59
 
60
- require "logster/rails/railtie" if defined?(::Rails::VERSION) && ::Rails::VERSION::MAJOR.to_i >= 3
60
+ require "logster/rails/railtie" if defined?(::Rails::VERSION)