log_android.go 1.9 KB

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