Loading apps/demo/src/pages/logcat.tsx +86 −2 Original line number Diff line number Diff line Loading @@ -96,6 +96,7 @@ const state = makeAutoObservable( format: LogcatFormat.ThreadTime, formatModifierUid: false, formatModifierTimezone: false, formatTime: "default" as "year" | "default" | "epoch" | "monotonic", formatNanosecond: "millisecond" as | "millisecond" Loading @@ -110,6 +111,7 @@ const state = makeAutoObservable( monotonic: this.formatTime === "monotonic", microseconds: this.formatNanosecond === "microsecond", nanoseconds: this.formatNanosecond === "nanosecond", timezone: this.formatModifierTimezone, }); }, Loading Loading @@ -267,6 +269,48 @@ const state = makeAutoObservable( this.format = LogcatFormat.Brief; }), }, { key: "process", text: "Process", canCheck: true, itemProps: { radioGroup: "format", }, checked: this.format === LogcatFormat.Process, onClick: action((e) => { e?.preventDefault(); e?.stopPropagation(); this.format = LogcatFormat.Process; }), }, { key: "tag", text: "Tag", canCheck: true, itemProps: { radioGroup: "format", }, checked: this.format === LogcatFormat.Tag, onClick: action((e) => { e?.preventDefault(); e?.stopPropagation(); this.format = LogcatFormat.Tag; }), }, { key: "thread", text: "Thread", canCheck: true, itemProps: { radioGroup: "format", }, checked: this.format === LogcatFormat.Thread, onClick: action((e) => { e?.preventDefault(); e?.stopPropagation(); this.format = LogcatFormat.Thread; }), }, { key: "raw", text: "Raw", Loading @@ -282,7 +326,21 @@ const state = makeAutoObservable( }), }, { key: "threadTime", key: "time", text: "Time", canCheck: true, itemProps: { radioGroup: "format", }, checked: this.format === LogcatFormat.Time, onClick: action((e) => { e?.preventDefault(); e?.stopPropagation(); this.format = LogcatFormat.Time; }), }, { key: "thread-time", text: "ThreadTime", canCheck: true, itemProps: { Loading @@ -296,6 +354,20 @@ const state = makeAutoObservable( this.format = LogcatFormat.ThreadTime; }), }, { key: "long", text: "Long", canCheck: true, itemProps: { radioGroup: "format", }, checked: this.format === LogcatFormat.Long, onClick: action((e) => { e?.preventDefault(); e?.stopPropagation(); this.format = LogcatFormat.Long; }), }, { key: "modifiers", Loading @@ -314,9 +386,21 @@ const state = makeAutoObservable( !this.formatModifierUid; }), }, { key: "timezone", text: "Timezone", canCheck: true, checked: this.formatModifierTimezone, onClick: action((e) => { e?.preventDefault(); e?.stopPropagation(); this.formatModifierTimezone = !this.formatModifierTimezone; }), }, { key: "time", key: "time-header", text: "Time Format", itemType: ContextualMenuItemType.Header, }, Loading libraries/android-bin/src/logcat.ts +146 −48 Original line number Diff line number Diff line Loading @@ -72,7 +72,7 @@ export interface LogcatFormatModifiers { nanoseconds?: boolean; printable?: boolean; year?: boolean; zone?: boolean; timezone?: boolean; epoch?: boolean; monotonic?: boolean; uid?: boolean; Loading Loading @@ -116,10 +116,7 @@ export interface AndroidLogEntry extends LoggerEntry { toString(format?: LogcatFormat, modifiers?: LogcatFormatModifiers): string; } function secondsToTimeString( seconds: number, modifiers: LogcatFormatModifiers ) { function formatSeconds(seconds: number, modifiers: LogcatFormatModifiers) { if (modifiers.monotonic) { return seconds.toString().padStart(6); } Loading @@ -128,102 +125,151 @@ function secondsToTimeString( return seconds.toString().padStart(19); } const time = new Date(seconds * 1000); const date = new Date(seconds * 1000); if (modifiers.year) { // prettier-ignore return `${ time.getFullYear().toString().padStart(4, "0") date.getFullYear().toString().padStart(4, "0") }-${ (time.getMonth() + 1).toString().padStart(2, "0") (date.getMonth() + 1).toString().padStart(2, "0") }-${ time.getDate().toString().padStart(2, "0") date.getDate().toString().padStart(2, "0") } ${ time.getHours().toString().padStart(2, "0") date.getHours().toString().padStart(2, "0") }:${ time.getMinutes().toString().padStart(2, "0") date.getMinutes().toString().padStart(2, "0") }:${ time.getSeconds().toString().padStart(2, "0") date.getSeconds().toString().padStart(2, "0") }`; } // prettier-ignore return `${ (time.getMonth() + 1).toString().padStart(2, "0") (date.getMonth() + 1).toString().padStart(2, "0") }-${ time.getDate().toString().padStart(2, "0") date.getDate().toString().padStart(2, "0") } ${ time.getHours().toString().padStart(2, "0") date.getHours().toString().padStart(2, "0") }:${ time.getMinutes().toString().padStart(2, "0") date.getMinutes().toString().padStart(2, "0") }:${ time.getSeconds().toString().padStart(2, "0") date.getSeconds().toString().padStart(2, "0") }`; } function timestampToTimeString( seconds: number, function formatNanoseconds( nanoseconds: number, modifiers: LogcatFormatModifiers ) { const wholePart = secondsToTimeString(seconds, modifiers); if (modifiers.nanoseconds) { // prettier-ignore return `${ wholePart }.${ nanoseconds.toString().padStart(9, "0") }`; return nanoseconds.toString().padStart(9, "0"); } if (modifiers.microseconds) { // prettier-ignore return `${ wholePart }.${ (nanoseconds / 1000 | 0).toString().padStart(6, "0") }`; return ((nanoseconds / 1000) | 0).toString().padStart(6, "0"); } return ((nanoseconds / 1000000) | 0).toString().padStart(3, "0"); } function formatTimezone(seconds: number, modifiers: LogcatFormatModifiers) { if (!modifiers.timezone || modifiers.monotonic || modifiers.epoch) { return ""; } const date = new Date(seconds * 1000); const offset = date.getTimezoneOffset(); const sign = offset <= 0 ? "+" : "-"; const absolute = Math.abs(offset); const hours = (absolute / 60) | 0; const minutes = absolute % 60; // prettier-ignore return ` ${ wholePart }.${ (nanoseconds / 1000000 | 0).toString().padStart(3, "0") sign }${ hours.toString().padStart(2, "0") }:${ minutes.toString().padStart(2, "0") }`; } function entryToPrefix( function formatTime( seconds: number, nanoseconds: number, modifiers: LogcatFormatModifiers ) { const secondsString = formatSeconds(seconds, modifiers); const nanosecondsString = formatNanoseconds(nanoseconds, modifiers); const zoneString = formatTimezone(seconds, modifiers); return `${secondsString}.${nanosecondsString}${zoneString}`; } function formatUid( uid: number, modifiers: LogcatFormatModifiers, suffix: string ) { return modifiers.uid ? `${uid.toString().padStart(5)}${suffix}` : ""; } function getFormatPrefix( entry: AndroidLogEntry, format: LogcatFormat, modifiers: LogcatFormatModifiers ) { // https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/logprint.cpp;l=1415;drc=8dbf3b2bb6b6d1652d9797e477b9abd03278bb79 const uid = modifiers?.uid ? `${entry.uid.toString().padStart(5)}` : ""; switch (format) { // TODO: implement other formats case LogcatFormat.Brief: case LogcatFormat.Tag: // prettier-ignore return `${ AndroidLogPriorityToCharacter[entry.priority] }/${ entry.tag.padEnd(8) }: `; case LogcatFormat.Process: // prettier-ignore return `${ AndroidLogPriorityToCharacter[entry.priority] }(${ uid ? uid + ":" : "" formatUid(entry.uid, modifiers, ":") }${ entry.pid.toString().padStart(5) }): `; }) `; case LogcatFormat.Thread: // prettier-ignore return `${ AndroidLogPriorityToCharacter[entry.priority] }(${ formatUid(entry.uid, modifiers, ":") }${ entry.pid.toString().padStart(5) }:${ entry.tid.toString().padStart(5) }) `; case LogcatFormat.Raw: return ""; case LogcatFormat.Time: // prettier-ignore return `${ formatTime(entry.seconds, entry.nanoseconds, modifiers) } ${ AndroidLogPriorityToCharacter[entry.priority] }/${ entry.tag.padEnd(8) }(${ formatUid(entry.uid, modifiers, ":") }${ entry.pid.toString().padStart(5) }): `; case LogcatFormat.ThreadTime: default: // prettier-ignore return `${ timestampToTimeString(entry.seconds, entry.nanoseconds, modifiers) formatTime(entry.seconds, entry.nanoseconds, modifiers) } ${ uid ? uid + " " : "" formatUid(entry.uid, modifiers, " ") }${ entry.pid.toString().padStart(5) } ${ Loading @@ -233,7 +279,40 @@ function entryToPrefix( } ${ entry.tag.toString().padEnd(8) }: `; case LogcatFormat.Brief: default: // prettier-ignore return `${ AndroidLogPriorityToCharacter[entry.priority] }/${ entry.tag.padEnd(8) }(${ formatUid(entry.uid, modifiers, ":") }${ entry.pid.toString().padStart(5) }): `; } } function getFormatSuffix(entry: AndroidLogEntry, format: LogcatFormat) { switch (format) { case LogcatFormat.Process: return ` (${entry.tag})`; default: return ""; } } function formatEntryWrapLine( entry: AndroidLogEntry, format: LogcatFormat, modifiers: LogcatFormatModifiers ) { const prefix = getFormatPrefix(entry, format, modifiers); const suffix = getFormatSuffix(entry, format); return ( prefix + entry.message.replaceAll("\n", suffix + "\n" + prefix) + suffix ); } function AndroidLogEntryToString( Loading @@ -241,8 +320,27 @@ function AndroidLogEntryToString( format: LogcatFormat = LogcatFormat.ThreadTime, modifiers: LogcatFormatModifiers = {} ) { const prefix = entryToPrefix(this, format, modifiers); return prefix + this.message.replaceAll("\n", "\n" + prefix); switch (format) { case LogcatFormat.Long: // prettier-ignore return `[ ${ formatTime(this.seconds, this.nanoseconds, modifiers) } ${ formatUid(this.uid, modifiers, ":") }${ this.pid.toString().padStart(5) }:${ this.tid.toString().padStart(5) } ${ AndroidLogPriorityToCharacter[this.priority] }/${ this.tag.padEnd(8) } ]\n${ this.message }\n`; default: return formatEntryWrapLine(this, format, modifiers); } } function findTagEnd(payload: Uint8Array) { Loading Loading
apps/demo/src/pages/logcat.tsx +86 −2 Original line number Diff line number Diff line Loading @@ -96,6 +96,7 @@ const state = makeAutoObservable( format: LogcatFormat.ThreadTime, formatModifierUid: false, formatModifierTimezone: false, formatTime: "default" as "year" | "default" | "epoch" | "monotonic", formatNanosecond: "millisecond" as | "millisecond" Loading @@ -110,6 +111,7 @@ const state = makeAutoObservable( monotonic: this.formatTime === "monotonic", microseconds: this.formatNanosecond === "microsecond", nanoseconds: this.formatNanosecond === "nanosecond", timezone: this.formatModifierTimezone, }); }, Loading Loading @@ -267,6 +269,48 @@ const state = makeAutoObservable( this.format = LogcatFormat.Brief; }), }, { key: "process", text: "Process", canCheck: true, itemProps: { radioGroup: "format", }, checked: this.format === LogcatFormat.Process, onClick: action((e) => { e?.preventDefault(); e?.stopPropagation(); this.format = LogcatFormat.Process; }), }, { key: "tag", text: "Tag", canCheck: true, itemProps: { radioGroup: "format", }, checked: this.format === LogcatFormat.Tag, onClick: action((e) => { e?.preventDefault(); e?.stopPropagation(); this.format = LogcatFormat.Tag; }), }, { key: "thread", text: "Thread", canCheck: true, itemProps: { radioGroup: "format", }, checked: this.format === LogcatFormat.Thread, onClick: action((e) => { e?.preventDefault(); e?.stopPropagation(); this.format = LogcatFormat.Thread; }), }, { key: "raw", text: "Raw", Loading @@ -282,7 +326,21 @@ const state = makeAutoObservable( }), }, { key: "threadTime", key: "time", text: "Time", canCheck: true, itemProps: { radioGroup: "format", }, checked: this.format === LogcatFormat.Time, onClick: action((e) => { e?.preventDefault(); e?.stopPropagation(); this.format = LogcatFormat.Time; }), }, { key: "thread-time", text: "ThreadTime", canCheck: true, itemProps: { Loading @@ -296,6 +354,20 @@ const state = makeAutoObservable( this.format = LogcatFormat.ThreadTime; }), }, { key: "long", text: "Long", canCheck: true, itemProps: { radioGroup: "format", }, checked: this.format === LogcatFormat.Long, onClick: action((e) => { e?.preventDefault(); e?.stopPropagation(); this.format = LogcatFormat.Long; }), }, { key: "modifiers", Loading @@ -314,9 +386,21 @@ const state = makeAutoObservable( !this.formatModifierUid; }), }, { key: "timezone", text: "Timezone", canCheck: true, checked: this.formatModifierTimezone, onClick: action((e) => { e?.preventDefault(); e?.stopPropagation(); this.formatModifierTimezone = !this.formatModifierTimezone; }), }, { key: "time", key: "time-header", text: "Time Format", itemType: ContextualMenuItemType.Header, }, Loading
libraries/android-bin/src/logcat.ts +146 −48 Original line number Diff line number Diff line Loading @@ -72,7 +72,7 @@ export interface LogcatFormatModifiers { nanoseconds?: boolean; printable?: boolean; year?: boolean; zone?: boolean; timezone?: boolean; epoch?: boolean; monotonic?: boolean; uid?: boolean; Loading Loading @@ -116,10 +116,7 @@ export interface AndroidLogEntry extends LoggerEntry { toString(format?: LogcatFormat, modifiers?: LogcatFormatModifiers): string; } function secondsToTimeString( seconds: number, modifiers: LogcatFormatModifiers ) { function formatSeconds(seconds: number, modifiers: LogcatFormatModifiers) { if (modifiers.monotonic) { return seconds.toString().padStart(6); } Loading @@ -128,102 +125,151 @@ function secondsToTimeString( return seconds.toString().padStart(19); } const time = new Date(seconds * 1000); const date = new Date(seconds * 1000); if (modifiers.year) { // prettier-ignore return `${ time.getFullYear().toString().padStart(4, "0") date.getFullYear().toString().padStart(4, "0") }-${ (time.getMonth() + 1).toString().padStart(2, "0") (date.getMonth() + 1).toString().padStart(2, "0") }-${ time.getDate().toString().padStart(2, "0") date.getDate().toString().padStart(2, "0") } ${ time.getHours().toString().padStart(2, "0") date.getHours().toString().padStart(2, "0") }:${ time.getMinutes().toString().padStart(2, "0") date.getMinutes().toString().padStart(2, "0") }:${ time.getSeconds().toString().padStart(2, "0") date.getSeconds().toString().padStart(2, "0") }`; } // prettier-ignore return `${ (time.getMonth() + 1).toString().padStart(2, "0") (date.getMonth() + 1).toString().padStart(2, "0") }-${ time.getDate().toString().padStart(2, "0") date.getDate().toString().padStart(2, "0") } ${ time.getHours().toString().padStart(2, "0") date.getHours().toString().padStart(2, "0") }:${ time.getMinutes().toString().padStart(2, "0") date.getMinutes().toString().padStart(2, "0") }:${ time.getSeconds().toString().padStart(2, "0") date.getSeconds().toString().padStart(2, "0") }`; } function timestampToTimeString( seconds: number, function formatNanoseconds( nanoseconds: number, modifiers: LogcatFormatModifiers ) { const wholePart = secondsToTimeString(seconds, modifiers); if (modifiers.nanoseconds) { // prettier-ignore return `${ wholePart }.${ nanoseconds.toString().padStart(9, "0") }`; return nanoseconds.toString().padStart(9, "0"); } if (modifiers.microseconds) { // prettier-ignore return `${ wholePart }.${ (nanoseconds / 1000 | 0).toString().padStart(6, "0") }`; return ((nanoseconds / 1000) | 0).toString().padStart(6, "0"); } return ((nanoseconds / 1000000) | 0).toString().padStart(3, "0"); } function formatTimezone(seconds: number, modifiers: LogcatFormatModifiers) { if (!modifiers.timezone || modifiers.monotonic || modifiers.epoch) { return ""; } const date = new Date(seconds * 1000); const offset = date.getTimezoneOffset(); const sign = offset <= 0 ? "+" : "-"; const absolute = Math.abs(offset); const hours = (absolute / 60) | 0; const minutes = absolute % 60; // prettier-ignore return ` ${ wholePart }.${ (nanoseconds / 1000000 | 0).toString().padStart(3, "0") sign }${ hours.toString().padStart(2, "0") }:${ minutes.toString().padStart(2, "0") }`; } function entryToPrefix( function formatTime( seconds: number, nanoseconds: number, modifiers: LogcatFormatModifiers ) { const secondsString = formatSeconds(seconds, modifiers); const nanosecondsString = formatNanoseconds(nanoseconds, modifiers); const zoneString = formatTimezone(seconds, modifiers); return `${secondsString}.${nanosecondsString}${zoneString}`; } function formatUid( uid: number, modifiers: LogcatFormatModifiers, suffix: string ) { return modifiers.uid ? `${uid.toString().padStart(5)}${suffix}` : ""; } function getFormatPrefix( entry: AndroidLogEntry, format: LogcatFormat, modifiers: LogcatFormatModifiers ) { // https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/logprint.cpp;l=1415;drc=8dbf3b2bb6b6d1652d9797e477b9abd03278bb79 const uid = modifiers?.uid ? `${entry.uid.toString().padStart(5)}` : ""; switch (format) { // TODO: implement other formats case LogcatFormat.Brief: case LogcatFormat.Tag: // prettier-ignore return `${ AndroidLogPriorityToCharacter[entry.priority] }/${ entry.tag.padEnd(8) }: `; case LogcatFormat.Process: // prettier-ignore return `${ AndroidLogPriorityToCharacter[entry.priority] }(${ uid ? uid + ":" : "" formatUid(entry.uid, modifiers, ":") }${ entry.pid.toString().padStart(5) }): `; }) `; case LogcatFormat.Thread: // prettier-ignore return `${ AndroidLogPriorityToCharacter[entry.priority] }(${ formatUid(entry.uid, modifiers, ":") }${ entry.pid.toString().padStart(5) }:${ entry.tid.toString().padStart(5) }) `; case LogcatFormat.Raw: return ""; case LogcatFormat.Time: // prettier-ignore return `${ formatTime(entry.seconds, entry.nanoseconds, modifiers) } ${ AndroidLogPriorityToCharacter[entry.priority] }/${ entry.tag.padEnd(8) }(${ formatUid(entry.uid, modifiers, ":") }${ entry.pid.toString().padStart(5) }): `; case LogcatFormat.ThreadTime: default: // prettier-ignore return `${ timestampToTimeString(entry.seconds, entry.nanoseconds, modifiers) formatTime(entry.seconds, entry.nanoseconds, modifiers) } ${ uid ? uid + " " : "" formatUid(entry.uid, modifiers, " ") }${ entry.pid.toString().padStart(5) } ${ Loading @@ -233,7 +279,40 @@ function entryToPrefix( } ${ entry.tag.toString().padEnd(8) }: `; case LogcatFormat.Brief: default: // prettier-ignore return `${ AndroidLogPriorityToCharacter[entry.priority] }/${ entry.tag.padEnd(8) }(${ formatUid(entry.uid, modifiers, ":") }${ entry.pid.toString().padStart(5) }): `; } } function getFormatSuffix(entry: AndroidLogEntry, format: LogcatFormat) { switch (format) { case LogcatFormat.Process: return ` (${entry.tag})`; default: return ""; } } function formatEntryWrapLine( entry: AndroidLogEntry, format: LogcatFormat, modifiers: LogcatFormatModifiers ) { const prefix = getFormatPrefix(entry, format, modifiers); const suffix = getFormatSuffix(entry, format); return ( prefix + entry.message.replaceAll("\n", suffix + "\n" + prefix) + suffix ); } function AndroidLogEntryToString( Loading @@ -241,8 +320,27 @@ function AndroidLogEntryToString( format: LogcatFormat = LogcatFormat.ThreadTime, modifiers: LogcatFormatModifiers = {} ) { const prefix = entryToPrefix(this, format, modifiers); return prefix + this.message.replaceAll("\n", "\n" + prefix); switch (format) { case LogcatFormat.Long: // prettier-ignore return `[ ${ formatTime(this.seconds, this.nanoseconds, modifiers) } ${ formatUid(this.uid, modifiers, ":") }${ this.pid.toString().padStart(5) }:${ this.tid.toString().padStart(5) } ${ AndroidLogPriorityToCharacter[this.priority] }/${ this.tag.padEnd(8) } ]\n${ this.message }\n`; default: return formatEntryWrapLine(this, format, modifiers); } } function findTagEnd(payload: Uint8Array) { Loading