1// Copyright 2023 The Pigweed Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); you may not 4// use this file except in compliance with the License. You may obtain a copy of 5// the License at 6// 7// https://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12// License for the specific language governing permissions and limitations under 13// the License. 14 15import { Detokenizer } from '../pw_tokenizer/ts'; 16import { LogSource } from '../pw_web/log-viewer/src/log-source'; 17import { Device } from './device'; 18import { LogEntry } from './logging'; 19 20export class PigweedRPCLogSource extends LogSource { 21 private detokenizer: Detokenizer | undefined; 22 private logs: LogEntry[] = []; 23 private call: any; 24 constructor(device: Device, tokenDB: string | undefined) { 25 super(); 26 if (tokenDB && tokenDB.length > 0) { 27 this.detokenizer = new Detokenizer(tokenDB); 28 } 29 this.call = device.rpcs.pw.log.Logs.Listen((msg: any) => { 30 msg 31 .getEntriesList() 32 .forEach((entry: any) => this.processFrame(entry.getMessage())); 33 }); 34 } 35 36 destroy() { 37 this.call.cancel(); 38 } 39 40 private processFrame(frame: Uint8Array) { 41 let entry: LogEntry; 42 if (this.detokenizer) { 43 const detokenized = this.detokenizer.detokenizeUint8Array(frame); 44 entry = this.parseLogMsg(detokenized); 45 } else { 46 const decoded = new TextDecoder().decode(frame); 47 entry = this.parseLogMsg(decoded); 48 } 49 this.logs = [...this.logs, entry]; 50 this.publishLogEntry(entry); 51 } 52 53 private parseLogMsg(msg: string): LogEntry { 54 const pairs = msg 55 .split('■') 56 .slice(1) 57 .map((pair) => pair.split('♦')); 58 59 // Not a valid message, print as-is. 60 const timestamp = new Date(); 61 if (pairs.length === 0) { 62 return { 63 fields: [ 64 { key: 'timestamp', value: timestamp.toISOString() }, 65 { key: 'message', value: msg }, 66 ], 67 timestamp: timestamp, 68 }; 69 } 70 71 const map: any = {}; 72 pairs.forEach((pair) => (map[pair[0]] = pair[1])); 73 return { 74 fields: [ 75 { key: 'timestamp', value: timestamp }, 76 { key: 'message', value: map.msg }, 77 { key: 'module', value: map.module }, 78 { key: 'file', value: map.file }, 79 ], 80 timestamp: timestamp, 81 }; 82 } 83} 84