github.com/cybriq/giocore@v0.0.7-0.20210703034601-cfb9cb5f3900/app/internal/log/log_android.go (about)

     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  }