| // Copyright 2023 The Pigweed Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| |
| import { Detokenizer } from '../pw_tokenizer/ts'; |
| import { LogSource } from '../pw_web/log-viewer/src/log-source'; |
| import { Device } from './device'; |
| import { LogEntry } from './logging'; |
| |
| export class PigweedRPCLogSource extends LogSource { |
| private detokenizer: Detokenizer | undefined; |
| private logs: LogEntry[] = []; |
| private call: any; |
| constructor(device: Device, tokenDB: string | undefined) { |
| super(); |
| if (tokenDB && tokenDB.length > 0) { |
| this.detokenizer = new Detokenizer(tokenDB); |
| } |
| this.call = device.rpcs.pw.log.Logs.Listen((msg: any) => { |
| msg |
| .getEntriesList() |
| .forEach((entry: any) => this.processFrame(entry.getMessage())); |
| }); |
| } |
| |
| destroy() { |
| this.call.cancel(); |
| } |
| |
| private processFrame(frame: Uint8Array) { |
| let entry: LogEntry; |
| if (this.detokenizer) { |
| const detokenized = this.detokenizer.detokenizeUint8Array(frame); |
| entry = this.parseLogMsg(detokenized); |
| } else { |
| const decoded = new TextDecoder().decode(frame); |
| entry = this.parseLogMsg(decoded); |
| } |
| this.logs = [...this.logs, entry]; |
| this.publishLogEntry(entry); |
| } |
| |
| private parseLogMsg(msg: string): LogEntry { |
| const pairs = msg |
| .split('■') |
| .slice(1) |
| .map((pair) => pair.split('♦')); |
| |
| // Not a valid message, print as-is. |
| const timestamp = new Date(); |
| if (pairs.length === 0) { |
| return { |
| fields: [ |
| { key: 'timestamp', value: timestamp.toISOString() }, |
| { key: 'message', value: msg }, |
| ], |
| timestamp: timestamp, |
| }; |
| } |
| |
| const map: any = {}; |
| pairs.forEach((pair) => (map[pair[0]] = pair[1])); |
| return { |
| fields: [ |
| { key: 'timestamp', value: timestamp }, |
| { key: 'message', value: map.msg }, |
| { key: 'module', value: map.module }, |
| { key: 'file', value: map.file }, |
| ], |
| timestamp: timestamp, |
| }; |
| } |
| } |