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