log_android.go raw
1 // SPDX-License-Identifier: Unlicense OR MIT
2
3 package log
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(appID)
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 }
88