]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: carbonize logs component
authorAfreen Misbah <afreen@ibm.com>
Sun, 17 May 2026 21:18:11 +0000 (02:48 +0530)
committerAfreen Misbah <afreen@ibm.com>
Mon, 18 May 2026 07:43:39 +0000 (13:13 +0530)
Fixes https://tracker.ceph.com/issues/68260

Assisted-by: Claude
Signed-off-by: Afreen Misbah <afreenmisbah@ibm.com>
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/logs/logs.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/logs/logs.component.scss
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/logs/logs.component.ts

index f5cf5ddb0d0154153bb8401d58eede4fb4ec8573..9fed18accfbcd2671e6a29c5a79629e54ef48473 100644 (file)
       <a ngbNavLink
          i18n>Cluster Logs</a>
       <ng-template ngbNavContent>
-        <div class="card bg-light mb-3"
-             *ngIf="clog">
-          <div class="btn-group"
-               role="group"
-               *ngIf="clog.length && showClusterLogs">
+        @if (clog) {
+        <div class="log-viewer"
+             [class.log-viewer--scrollable]="scrollable">
+          @if (clog.length && showClusterLogs && showDownloadCopyButton) {
+          <div class="log-actions">
             <cd-download-button [objectItem]="clog"
                                 [textItem]="clogText"
-                                fileName="cluster_log"
-                                *ngIf="showDownloadCopyButton">
+                                fileName="cluster_log">
             </cd-download-button>
-            <cd-copy-2-clipboard-button
-                    [source]="clogText"
-                    [byId]="false"
-                    *ngIf="showDownloadCopyButton">
+            <cd-copy-2-clipboard-button [source]="clogText"
+                                        [byId]="false">
             </cd-copy-2-clipboard-button>
           </div>
-          <div class="card-body"
-               [ngClass]="{'overflow-auto': scrollable}">
-            <p *ngFor="let line of clog">
-              <span class="timestamp">{{ line.stamp | cdDate }}</span>
-              <span class="priority {{ line.priority | logPriority }}">{{ line.priority }}</span>
-              <span class="message"
-                    [innerHTML]="line.message | searchHighlight: search"></span>
-            </p>
-
-            <ng-container *ngIf="clog.length !== 0 else noEntriesTpl"></ng-container>
+          }
+
+          <div class="log-entries">
+            @for (line of clog; track trackByLogEntry($index, line)) {
+            <div class="log-entry cds--type-code-01">
+              <span class="log-entry__timestamp ">{{ line.stamp | cdDate }}</span>
+              <span class="log-entry__priority"
+                    [ngClass]="'log-entry__priority--' + (line.priority | logPriority)">
+                {{ line.priority }}
+              </span>
+              <span class="log-entry__message"
+                    [innerHTML]="line.message | searchHighlight: search">
+              </span>
+            </div>
+            }
+
+            @if (clog.length === 0) {
+            <div class="log-viewer__empty">
+              <ng-container *ngTemplateOutlet="noEntriesTpl"></ng-container>
+            </div>
+            }
           </div>
         </div>
+        }
       </ng-template>
     </ng-container>
     <ng-container ngbNavItem="audit-logs">
       <a ngbNavLink
          i18n>Audit Logs</a>
       <ng-template ngbNavContent>
-        <div class="card bg-light mb-3"
-             *ngIf="audit_log && showAuditLogs">
-          <div class="btn-group"
-               role="group"
-               *ngIf="audit_log.length">
+        @if (audit_log && showAuditLogs) {
+        <div class="log-viewer">
+          @if (audit_log.length && showDownloadCopyButton) {
+          <div class="log-actions">
             <cd-download-button [objectItem]="audit_log"
                                 [textItem]="auditLogText"
-                                fileName="audit_log"
-                                *ngIf="showDownloadCopyButton">
+                                fileName="audit_log">
             </cd-download-button>
-            <cd-copy-2-clipboard-button
-                    [source]="auditLogText"
-                    [byId]="false"
-                    *ngIf="showDownloadCopyButton">
+            <cd-copy-2-clipboard-button [source]="auditLogText"
+                                        [byId]="false">
             </cd-copy-2-clipboard-button>
           </div>
-          <div class="card-body">
-            <p *ngFor="let line of audit_log">
-              <span class="timestamp">{{ line.stamp | cdDate }}</span>
-              <span class="priority {{ line.priority | logPriority }}">{{ line.priority }}</span>
-              <span class="message"
-                    [innerHTML]="line.message | searchHighlight: search"></span>
-            </p>
-
-            <ng-container *ngIf="audit_log.length !== 0 else noEntriesTpl"></ng-container>
+          }
+
+          <div class="log-entries">
+            @for (line of audit_log; track trackByLogEntry($index, line)) {
+            <div class="log-entry cds--type-code-01">
+              <span class="log-entry__timestamp">{{ line.stamp | cdDate }}</span>
+              <span class="log-entry__priority"
+                    [ngClass]="'log-entry__priority--' + (line.priority | logPriority)">
+                {{ line.priority }}
+              </span>
+              <span class="log-entry__message"
+                    [innerHTML]="line.message | searchHighlight: search">
+              </span>
+            </div>
+            }
+
+            @if (audit_log.length === 0) {
+            <div class="log-viewer__empty">
+              <ng-container *ngTemplateOutlet="noEntriesTpl"></ng-container>
+            </div>
+            }
           </div>
         </div>
+        }
       </ng-template>
     </ng-container>
     <ng-container ngbNavItem="daemon-logs">
index 6e4a282070a0c7e673154d12e5426e17a7006ef7..2476726fc54a529477e225a8dc02cc304b868d2b 100644 (file)
-@use './src/styles/vendor/variables' as vv;
+// Log viewer container
+.log-viewer {
+  position: relative;
+  background: var(--cds-layer-01);
+  border-radius: var(--cds-spacing-02);
+  padding: var(--cds-spacing-05);
 
-p {
-  font-family: monospace;
-}
+  &--scrollable {
+    max-height: 50vh;
+    overflow-y: auto;
 
-.card {
-  .btn-group {
-    margin-top: -57px;
-    position: absolute;
-    right: 0;
-  }
+    // Custom scrollbar styling for better Carbon integration
+    &::-webkit-scrollbar {
+      width: 8px;
+    }
 
-  div p {
-    display: flex;
+    &::-webkit-scrollbar-track {
+      background: var(--cds-layer-01);
+    }
+
+    &::-webkit-scrollbar-thumb {
+      background: var(--cds-border-subtle-01);
+      border-radius: var(--cds-spacing-02);
 
-    &:last-child {
-      margin-bottom: 0;
+      &:hover {
+        background: var(--cds-border-strong-01);
+      }
     }
   }
 
-  .timestamp {
-    flex-shrink: 0;
-    font-weight: bold;
+  &__empty {
+    font-size: var(--cds-body-compact-01-font-size);
+    font-weight: var(--cds-body-compact-01-font-weight);
+    line-height: var(--cds-body-compact-01-line-height);
+    letter-spacing: var(--cds-body-compact-01-letter-spacing);
+    color: var(--cds-text-secondary);
+    padding: var(--cds-spacing-07) 0;
+    text-align: center;
   }
+}
 
-  .priority {
-    margin-left: 0.5rem;
-  }
+// Action buttons (download/copy)
+.log-actions {
+  position: absolute;
+  top: var(--cds-spacing-03);
+  right: var(--cds-spacing-03);
+  display: flex;
+  gap: var(--cds-spacing-02);
+  z-index: 1;
+}
 
-  .message {
-    margin-left: 1rem;
-  }
+// Log entries container
+.log-entries {
+  display: flex;
+  flex-direction: column;
+  gap: var(--cds-spacing-02);
+}
 
-  .err {
-    color: vv.$danger;
-  }
+// Individual log entry
+.log-entry {
+  display: flex;
+  gap: var(--cds-spacing-04);
+  padding: var(--cds-spacing-02) 0;
 
-  .warn {
-    color: vv.$warning;
+  &:hover {
+    background: var(--cds-layer-hover-01);
+    margin: 0 calc(-1 * var(--cds-spacing-03));
+    padding: var(--cds-spacing-02) var(--cds-spacing-03);
+    border-radius: var(--cds-spacing-01);
   }
 
-  .info {
-    color: vv.$info;
+  &__timestamp {
+    flex-shrink: 0;
+    font-weight: 600;
+    color: var(--cds-text-primary);
+    min-width: 140px;
   }
 
-  .debug {
-    color: vv.$gray-700;
+  &__priority {
+    flex-shrink: 0;
+    font-weight: 500;
+    min-width: 48px;
+
+    &--err {
+      color: var(--cds-support-error);
+    }
+
+    &--warn {
+      color: var(--cds-support-warning);
+    }
+
+    &--info {
+      color: var(--cds-support-info);
+    }
+
+    &--debug {
+      color: var(--cds-text-secondary);
+    }
   }
-}
 
-::ng-deep cd-logs ngb-timepicker input.ngb-tp-input {
-  width: 3.5rem !important;
+  &__message {
+    flex: 1;
+    overflow-wrap: break-word;
+    color: var(--cds-text-primary);
+  }
 }
 
-.card-body.overflow-auto {
-  height: 50vh;
+.log-entry__message mark {
+  background-color: var(--cds-background-selected);
+  color: var(--cds-text-on-color);
+  padding: 0 var(--cds-spacing-01);
+  border-radius: var(--cds-spacing-01);
 }
index 2feac9cb91b42f150bea14fb9517f4bf31827b61..f9d13d7585c4cd2fbc94622d70fb6e58d36651f5 100644 (file)
@@ -1,5 +1,5 @@
 import { DatePipe } from '@angular/common';
-import { Component, Input, NgZone, OnDestroy, OnInit } from '@angular/core';
+import { Component, Input, NgZone, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
 
 import { NgbDateStruct } from '@ng-bootstrap/ng-bootstrap';
 import { Observable } from 'rxjs';
@@ -13,6 +13,7 @@ import { Icons } from '~/app/shared/enum/icons.enum';
   selector: 'cd-logs',
   templateUrl: './logs.component.html',
   styleUrls: ['./logs.component.scss'],
+  encapsulation: ViewEncapsulation.None,
   standalone: false
 })
 export class LogsComponent implements OnInit, OnDestroy {
@@ -192,4 +193,8 @@ export class LogsComponent implements OnInit, OnDestroy {
     }
     return logText;
   }
+
+  trackByLogEntry(index: number, entry: any): string {
+    return `${entry.stamp}-${index}`;
+  }
 }