forked from joejulian/gio
e97adeedd9
This way, a Gio app's logs can be filtered uniquely, which wasn't possible before since the tag "gio" would be the same between gio apps. Since the app ID is supplied at build time, inject it via a variable with the linker's help. The variable is only used on Android for now, but that's OK. It might be useful for other platforms or other internal packages in the future. Fixes #84. Signed-off-by: Daniel Martí <mvdan@mvdan.cc>
82 lines
1.8 KiB
Go
82 lines
1.8 KiB
Go
// SPDX-License-Identifier: Unlicense OR MIT
|
|
|
|
package log
|
|
|
|
/*
|
|
#cgo LDFLAGS: -llog
|
|
|
|
#include <stdlib.h>
|
|
#include <android/log.h>
|
|
*/
|
|
import "C"
|
|
|
|
import (
|
|
"bufio"
|
|
"log"
|
|
"os"
|
|
"runtime"
|
|
"syscall"
|
|
"unsafe"
|
|
)
|
|
|
|
// 1024 is the truncation limit from android/log.h, plus a \n.
|
|
const logLineLimit = 1024
|
|
|
|
var logTag = C.CString(appID)
|
|
|
|
func init() {
|
|
// Android's logcat already includes timestamps.
|
|
log.SetFlags(log.Flags() &^ log.LstdFlags)
|
|
log.SetOutput(new(androidLogWriter))
|
|
|
|
// Redirect stdout and stderr to the Android logger.
|
|
logFd(os.Stdout.Fd())
|
|
logFd(os.Stderr.Fd())
|
|
}
|
|
|
|
type androidLogWriter struct {
|
|
// buf has room for the maximum log line, plus a terminating '\0'.
|
|
buf [logLineLimit + 1]byte
|
|
}
|
|
|
|
func (w *androidLogWriter) Write(data []byte) (int, error) {
|
|
// Truncate the buffer, leaving space for the '\0'.
|
|
if max := len(w.buf) - 1; len(data) > max {
|
|
data = data[:max]
|
|
}
|
|
buf := w.buf[:len(data)+1]
|
|
copy(buf, data)
|
|
// Terminating '\0'.
|
|
buf[len(data)] = 0
|
|
C.__android_log_write(C.ANDROID_LOG_INFO, logTag, (*C.char)(unsafe.Pointer(&buf[0])))
|
|
return len(data), nil
|
|
}
|
|
|
|
func logFd(fd uintptr) {
|
|
r, w, err := os.Pipe()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
if err := syscall.Dup3(int(w.Fd()), int(fd), syscall.O_CLOEXEC); err != nil {
|
|
panic(err)
|
|
}
|
|
go func() {
|
|
lineBuf := bufio.NewReaderSize(r, logLineLimit)
|
|
// The buffer to pass to C, including the terminating '\0'.
|
|
buf := make([]byte, lineBuf.Size()+1)
|
|
cbuf := (*C.char)(unsafe.Pointer(&buf[0]))
|
|
for {
|
|
line, _, err := lineBuf.ReadLine()
|
|
if err != nil {
|
|
break
|
|
}
|
|
copy(buf, line)
|
|
buf[len(line)] = 0
|
|
C.__android_log_write(C.ANDROID_LOG_INFO, logTag, cbuf)
|
|
}
|
|
// The garbage collector doesn't know that w's fd was dup'ed.
|
|
// Avoid finalizing w, and thereby avoid its finalizer closing its fd.
|
|
runtime.KeepAlive(w)
|
|
}()
|
|
}
|