aboutsummaryrefslogtreecommitdiff
path: root/pw_web/log-viewer/src/log-source.ts
diff options
context:
space:
mode:
Diffstat (limited to 'pw_web/log-viewer/src/log-source.ts')
-rw-r--r--pw_web/log-viewer/src/log-source.ts92
1 files changed, 84 insertions, 8 deletions
diff --git a/pw_web/log-viewer/src/log-source.ts b/pw_web/log-viewer/src/log-source.ts
index d000a46d0..6d46f54f8 100644
--- a/pw_web/log-viewer/src/log-source.ts
+++ b/pw_web/log-viewer/src/log-source.ts
@@ -12,28 +12,43 @@
// License for the specific language governing permissions and limitations under
// the License.
-import { LogEntry } from './shared/interfaces';
+import {
+ Field,
+ LogEntry,
+ LogSourceEvent,
+ SourceData,
+} from './shared/interfaces';
export abstract class LogSource {
private eventListeners: {
eventType: string;
- listener: (data: LogEntry) => void;
+ listener: (event: LogSourceEvent) => void;
}[];
- constructor() {
+ protected sourceId: string;
+
+ protected sourceName: string;
+
+ constructor(sourceName: string) {
this.eventListeners = [];
+ this.sourceId = crypto.randomUUID();
+ this.sourceName = sourceName;
}
+ abstract start(): void;
+
+ abstract stop(): void;
+
addEventListener(
eventType: string,
- listener: (data: LogEntry) => void,
+ listener: (event: LogSourceEvent) => void,
): void {
this.eventListeners.push({ eventType, listener });
}
removeEventListener(
eventType: string,
- listener: (data: LogEntry) => void,
+ listener: (event: LogSourceEvent) => void,
): void {
this.eventListeners = this.eventListeners.filter(
(eventListener) =>
@@ -42,11 +57,72 @@ export abstract class LogSource {
);
}
- emitEvent(eventType: string, data: LogEntry): void {
+ emitEvent(event: LogSourceEvent): void {
this.eventListeners.forEach((eventListener) => {
- if (eventListener.eventType === eventType) {
- eventListener.listener(data);
+ if (eventListener.eventType === event.type) {
+ eventListener.listener(event);
}
});
}
+
+ publishLogEntry(logEntry: LogEntry): void {
+ // Validate the log entry
+ const validationResult = this.validateLogEntry(logEntry);
+ if (validationResult !== null) {
+ console.error('Validation error:', validationResult);
+ return;
+ }
+
+ const sourceData: SourceData = { id: this.sourceId, name: this.sourceName };
+ logEntry.sourceData = sourceData;
+
+ // Add the name of the log source as a field in the log entry
+ const logSourceField: Field = { key: 'log_source', value: this.sourceName };
+ logEntry.fields.splice(1, 0, logSourceField);
+
+ this.emitEvent({ type: 'log-entry', data: logEntry });
+ }
+
+ validateLogEntry(logEntry: LogEntry): string | null {
+ try {
+ if (!logEntry.timestamp) {
+ return 'Log entry has no valid timestamp';
+ }
+ if (!Array.isArray(logEntry.fields)) {
+ return 'Log entry fields must be an array';
+ }
+ if (logEntry.fields.length === 0) {
+ return 'Log entry fields must not be empty';
+ }
+
+ for (const field of logEntry.fields) {
+ if (!field.key || typeof field.key !== 'string') {
+ return 'Invalid field key';
+ }
+ if (
+ field.value === undefined ||
+ (typeof field.value !== 'string' &&
+ typeof field.value !== 'boolean' &&
+ typeof field.value !== 'number' &&
+ typeof field.value !== 'object')
+ ) {
+ return 'Invalid field value';
+ }
+ }
+
+ if (
+ logEntry.severity !== undefined &&
+ typeof logEntry.severity !== 'string'
+ ) {
+ return 'Invalid severity value';
+ }
+
+ return null;
+ } catch (error) {
+ if (error instanceof Error) {
+ console.error('Validation error:', error.message);
+ }
+ return 'An unexpected error occurred during validation';
+ }
+ }
}