gioui.org@v0.6.1-0.20240506124620-7a9ce51988ce/app/log_android.go (about) 1 // SPDX-License-Identifier: Unlicense OR MIT 2 3 package app 4 5 /* 6 #cgo LDFLAGS: -llog 7 8 #include <stdlib.h> 9 #include <android/log.h> 10 */ 11 import "C" 12 13 import ( 14 "bufio" 15 "log" 16 "os" 17 "runtime" 18 "syscall" 19 "unsafe" 20 ) 21 22 // 1024 is the truncation limit from android/log.h, plus a \n. 23 const logLineLimit = 1024 24 25 var logTag = C.CString(ID) 26 27 func init() { 28 // Android's logcat already includes timestamps. 29 log.SetFlags(log.Flags() &^ log.LstdFlags) 30 log.SetOutput(new(androidLogWriter)) 31 32 // Redirect stdout and stderr to the Android logger. 33 logFd(os.Stdout.Fd()) 34 logFd(os.Stderr.Fd()) 35 } 36 37 type androidLogWriter struct { 38 // buf has room for the maximum log line, plus a terminating '\0'. 39 buf [logLineLimit + 1]byte 40 } 41 42 func (w *androidLogWriter) Write(data []byte) (int, error) { 43 n := 0 44 for len(data) > 0 { 45 msg := data 46 // Truncate the buffer, leaving space for the '\0'. 47 if max := len(w.buf) - 1; len(msg) > max { 48 msg = msg[:max] 49 } 50 buf := w.buf[:len(msg)+1] 51 copy(buf, msg) 52 // Terminating '\0'. 53 buf[len(msg)] = 0 54 C.__android_log_write(C.ANDROID_LOG_INFO, logTag, (*C.char)(unsafe.Pointer(&buf[0]))) 55 n += len(msg) 56 data = data[len(msg):] 57 } 58 return n, nil 59 } 60 61 func logFd(fd uintptr) { 62 r, w, err := os.Pipe() 63 if err != nil { 64 panic(err) 65 } 66 if err := syscall.Dup3(int(w.Fd()), int(fd), syscall.O_CLOEXEC); err != nil { 67 panic(err) 68 } 69 go func() { 70 lineBuf := bufio.NewReaderSize(r, logLineLimit) 71 // The buffer to pass to C, including the terminating '\0'. 72 buf := make([]byte, lineBuf.Size()+1) 73 cbuf := (*C.char)(unsafe.Pointer(&buf[0])) 74 for { 75 line, _, err := lineBuf.ReadLine() 76 if err != nil { 77 break 78 } 79 copy(buf, line) 80 buf[len(line)] = 0 81 C.__android_log_write(C.ANDROID_LOG_INFO, logTag, cbuf) 82 } 83 // The garbage collector doesn't know that w's fd was dup'ed. 84 // Avoid finalizing w, and thereby avoid its finalizer closing its fd. 85 runtime.KeepAlive(w) 86 }() 87 }