aboutsummaryrefslogtreecommitdiffstats
path: root/Godeps/_workspace/src/github.com/obscuren/qml/log.go
blob: 5301e63b0e458e594678bb885b3e5f570564f10f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package qml

// #include "capi.h"
//
import "C"

import (
    "fmt"
    "log"
    "path/filepath"
    "strings"
)

// SetLogger sets the target for messages logged by the qml package,
// including console.log and related calls from within qml code.
//
// The logger value must implement either the StdLogger interface,
// which is satisfied by the standard *log.Logger type, or the QmlLogger
// interface, which offers more control over the logged message.
//
// If no logger is provided, the qml package will send messages to the
// default log package logger. This behavior may also be restored by
// providing a nil logger to this function.
func SetLogger(logger interface{}) {
    if logger == nil {
        logHandler = defaultLogger{}
        return
    }
    if qmll, ok := logger.(QmlLogger); ok {
        logHandler = qmll
        return
    }
    if stdl, ok := logger.(StdLogger); ok {
        logHandler = wrappedStdLogger{stdl}
        return
    }
    panic("unsupported logger interface")
}

// The QmlLogger interface may be implemented to better control how
// log messages from the qml package are handled. Values that
// implement either StdLogger or QmlLogger may be provided to the
// SetLogger function.
type QmlLogger interface {
    // QmlOutput is called whenever a new message is available for logging.
    // The message value must not be used after the method returns.
    QmlOutput(message LogMessage) error
}

// The StdLogger interface is implemented by standard *log.Logger values.
// Values that implement either StdLogger or QmlLogger may be provided
// to the SetLogger function.
type StdLogger interface {
    // Output is called whenever a new message is available for logging.
    // See the standard log.Logger type for more details.
    Output(calldepth int, s string) error
}

// NOTE: LogMessage is an interface to avoid allocating and copying
// several strings for each logged message.

// LogMessage is implemented by values provided to QmlLogger.QmlOutput.
type LogMessage interface {
    Severity() LogSeverity
    Text() string
    File() string
    Line() int

    String() string // returns "file:line: text"

    privateMarker()
}

type LogSeverity int

const (
    LogDebug LogSeverity = iota
    LogWarning
    LogCritical
    LogFatal
)

var logHandler QmlLogger = defaultLogger{}

type defaultLogger struct{}

func (defaultLogger) QmlOutput(msg LogMessage) error {
    log.Println(msg.String())
    return nil
}

func init() {
    // Install the C++ log handler that diverts calls to the hook below.
    C.installLogHandler()
}

//export hookLogHandler
func hookLogHandler(cmsg *C.LogMessage) {
    // Workarund for QTBUG-35943
    text := unsafeString(cmsg.text, cmsg.textLen)
    if strings.HasPrefix(text, `"Qt Warning: Compose file:`) {
        return
    }
    msg := logMessage{c: cmsg}
    logHandler.QmlOutput(&msg)
    msg.invalid = true
}

type wrappedStdLogger struct {
    StdLogger
}

func (l wrappedStdLogger) QmlOutput(msg LogMessage) error {
    return l.Output(0, msg.String())
}

type logMessage struct {
    c *C.LogMessage

    // invalid flags that cmsg points to unreliable memory,
    // since the log hook has already returned.
    invalid bool
}

func (m *logMessage) assertValid() {
    if m.invalid {
        panic("attempted to use log message outside of log hook")
    }
}

func (m *logMessage) Severity() LogSeverity {
    return LogSeverity(m.c.severity)
}

func (m *logMessage) Line() int {
    m.assertValid()
    return int(m.c.line)
}

func (m *logMessage) String() string {
    m.assertValid()
    file := unsafeString(m.c.file, m.c.fileLen)
    text := unsafeString(m.c.text, m.c.textLen)
    return fmt.Sprintf("%s:%d: %s", filepath.Base(file), m.c.line, text)
}

func (m *logMessage) File() string {
    m.assertValid()
    return C.GoStringN(m.c.file, m.c.fileLen)
}

func (m *logMessage) Text() string {
    m.assertValid()
    return C.GoStringN(m.c.text, m.c.line)
}

func (*logMessage) privateMarker() {}