blob: 393393651e32d09720ae4c0909c8a388daee2d2c [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build android
package jni
import (
"bufio"
"bytes"
"fmt"
"log"
"os"
"runtime"
"unsafe"
"v.io/v23/logging"
)
// #include <android/log.h>
// #include <stdlib.h>
// #cgo LDFLAGS: -llog
import "C"
const (
initialMaxStackBufSize = 128 * 1024
maxStackBufSize = 4096 * 1024
)
var ctag *C.char = C.CString("GoLog")
func init() {
log.SetOutput(&androidWriter{})
r, w, err := os.Pipe()
if err != nil {
panic(err)
}
os.Stderr = w
go lineLog(r, C.ANDROID_LOG_ERROR)
r, w, err = os.Pipe()
if err != nil {
panic(err)
}
os.Stdout = w
go lineLog(r, C.ANDROID_LOG_INFO)
}
func header(depth int) string {
_, file, line, ok := runtime.Caller(3 + depth)
if !ok {
file = "???"
line = 1
}
return fmt.Sprintf("%s:%d", file, line)
}
func alog(severity C.int, depth int, args ...interface{}) {
writeLines(severity, header(depth) + " " + fmt.Sprint(args...))
}
func alogf(severity C.int, depth int, format string, args ...interface{}) {
writeLines(severity, header(depth) + " " + fmt.Sprintf(format, args...))
}
func writeLines(severity C.int, msg string) {
scanner := bufio.NewScanner(bytes.NewReader([]byte(msg)))
for scanner.Scan() {
write(severity, scanner.Text())
}
}
func write(severity C.int, msg string) {
cstr := C.CString(msg)
C.__android_log_write(severity, ctag, cstr)
C.free(unsafe.Pointer(cstr))
}
// NewLogger creates a new logger that uses Android logging functions.
func NewLogger(name string, level int) logging.Logger {
return &logger{name, level}
}
type logger struct {
name string
level int
}
func (l *logger) String() string {
return fmt.Sprintf("name=%s level=%d", l.name, l.level)
}
func (l *logger) Info(args ...interface{}) {
alog(C.ANDROID_LOG_INFO, 0, args...)
}
func (l *logger) Infof(format string, args ...interface{}) {
alogf(C.ANDROID_LOG_INFO, 0, format, args...)
}
func (l *logger) InfoDepth(depth int, args ...interface{}) {
alog(C.ANDROID_LOG_INFO, depth, args...)
}
func (l *logger) InfoStack(all bool) {
n := initialMaxStackBufSize
var trace []byte
for n <= maxStackBufSize {
trace = make([]byte, n)
nbytes := runtime.Stack(trace, all)
if nbytes < len(trace) {
alog(C.ANDROID_LOG_INFO, 0, string(trace[:nbytes]))
}
n *= 2
}
alog(C.ANDROID_LOG_INFO, 0, string(trace))
}
func (l *logger) Error(args ...interface{}) {
alog(C.ANDROID_LOG_ERROR, 0, args...)
}
func (l *logger) Errorf(format string, args ...interface{}) {
alogf(C.ANDROID_LOG_ERROR, 0, format, args...)
}
func (l *logger) ErrorDepth(depth int, args ...interface{}) {
alog(C.ANDROID_LOG_ERROR, depth, args...)
}
func (l *logger) Fatal(args ...interface{}) {
alog(C.ANDROID_LOG_FATAL, 0, args...)
l.InfoStack(true)
os.Exit(255)
}
func (l *logger) Fatalf(format string, args ...interface{}) {
alogf(C.ANDROID_LOG_FATAL, 0, format, args...)
l.InfoStack(true)
os.Exit(255)
}
func (l *logger) FatalDepth(depth int, args ...interface{}) {
alog(C.ANDROID_LOG_FATAL, depth, args...)
l.InfoStack(true)
os.Exit(255)
}
func (l *logger) Panic(args ...interface{}) {
l.Error(args...)
panic(fmt.Sprint(args...))
}
func (l *logger) Panicf(format string, args ...interface{}) {
l.Errorf(format, args...)
panic(fmt.Sprintf(format, args...))
}
func (l *logger) PanicDepth(depth int, args ...interface{}) {
l.ErrorDepth(depth, args...)
panic(fmt.Sprint(args...))
}
func (l *logger) V(v int) bool {
return v <= l.level
}
func (l *logger) VDepth(_ int, v int) bool {
return l.V(v)
}
type discardInfo struct{}
func (discardInfo) Info(...interface{}) {}
func (discardInfo) Infof(_ string, _ ...interface{}) {}
func (discardInfo) InfoDepth(_ int, _ ...interface{}) {}
func (discardInfo) InfoStack(_ bool) {}
func (l *logger) VI(v int) interface {
Info(args ...interface{})
Infof(format string, args ...interface{})
InfoDepth(depth int, args ...interface{})
InfoStack(all bool)
} {
if l.V(v) {
return l
}
return discardInfo{}
}
func (l *logger) VIDepth(depth int, v int) interface {
Info(args ...interface{})
Infof(format string, args ...interface{})
InfoDepth(depth int, args ...interface{})
InfoStack(all bool)
} {
if l.VDepth(depth, v) {
return l
}
return discardInfo{}
}
func (l *logger) FlushLog() {}
// androidWriter is used for diverting Go 'log' package output to android's INFO log.
type androidWriter struct {}
func (aw *androidWriter) Write(p []byte) (n int, err error) {
write(C.ANDROID_LOG_INFO, string(p))
return len(p), nil
}
// lineLog is used for diverting Go Stderr and Stdout to android's logs.
// NOTE(spetrovic): lifted from https://github.com/golang/mobile.
func lineLog(f *os.File, severity C.int) {
const logSize = 1024 // matches android/log.h.
r := bufio.NewReaderSize(f, logSize)
for {
line, _, err := r.ReadLine()
str := string(line)
if err != nil {
str += " " + err.Error()
}
write(severity, str)
if err != nil {
break
}
}
}