1 /******************************************************************************
2     QtAV:  Multimedia framework based on Qt and FFmpeg
3     Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
4 
5 *   This file is part of QtAV (from 2013)
6 
7     This library is free software; you can redistribute it and/or
8     modify it under the terms of the GNU Lesser General Public
9     License as published by the Free Software Foundation; either
10     version 2.1 of the License, or (at your option) any later version.
11 
12     This library is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15     Lesser General Public License for more details.
16 
17     You should have received a copy of the GNU Lesser General Public
18     License along with this library; if not, write to the Free Software
19     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20 ******************************************************************************/
21 
22 /*!
23  * DO NOT appear qDebug, qWanring etc in Logger.cpp! They are undefined and redefined to QtAV:Internal::Logger.xxx
24  */
25 // we need LogLevel so must include QtAV_Global.h
26 #include <QtCore/QString>
27 #include <QtCore/QMutex>
28 #include "QtAV/QtAV_Global.h"
29 #include "Logger.h"
30 
31 #ifndef QTAV_NO_LOG_LEVEL
32 
33 void ffmpeg_version_print();
34 namespace QtAV {
35 namespace Internal {
36 static QString gQtAVLogTag = QString();
37 
38 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
39 typedef Logger::Context QMessageLogger;
40 #endif
41 
log_helper(QtMsgType msgType,const QMessageLogger * qlog,const char * msg,va_list ap)42 static void log_helper(QtMsgType msgType, const QMessageLogger *qlog, const char* msg, va_list ap) {
43     static QMutex m;
44     QMutexLocker lock(&m);
45     Q_UNUSED(lock);
46     static int repeat = 0;
47     static QString last_msg;
48     static QtMsgType last_type = QtDebugMsg;
49     QString qmsg(gQtAVLogTag);
50     QString formated;
51     if (msg) {
52         formated = QString().vsprintf(msg, ap);
53     }
54     // repeate check
55     if (last_type == msgType && last_msg == formated) {
56         repeat++;
57         return;
58     }
59     if (repeat > 0) {
60         // print repeat message and current message
61         qmsg = QStringLiteral("%1(repeat %2)%3\n%4%5")
62                 .arg(qmsg).arg(repeat).arg(last_msg).arg(qmsg).arg(formated);
63     } else {
64         qmsg += formated;
65     }
66     repeat = 0;
67     last_type = msgType;
68     last_msg = formated;
69 
70     // qt_message_output is a public api
71 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
72     qt_message_output(msgType, qmsg.toUtf8().constData());
73     return;
74 #else
75     if (!qlog) {
76         QMessageLogContext ctx;
77         qt_message_output(msgType, ctx, qmsg);
78         return;
79     }
80 #endif
81 #ifndef QT_NO_DEBUG_STREAM
82     if (msgType == QtWarningMsg)
83         qlog->warning() << qmsg;
84     else if (msgType == QtCriticalMsg)
85         qlog->critical() << qmsg;
86     else if (msgType == QtFatalMsg)
87         qlog->fatal("%s", qmsg.toUtf8().constData());
88     else
89         qlog->debug() << qmsg;
90 #else
91     if (msgType == QtFatalMsg)
92         qlog->fatal("%s", qmsg.toUtf8().constData());
93 #endif //
94 }
95 
96 // macro does not support A::##X
97 
debug(const char * msg,...) const98 void Logger::debug(const char *msg, ...) const
99 {
100     QtAVDebug d; // initialize something. e.g. environment check
101     Q_UNUSED(d);
102     const int v = (int)logLevel();
103     if (v <= (int)LogOff)
104         return;
105     if (v > (int)LogDebug && v < (int)LogAll)
106         return;
107     va_list ap;
108     va_start(ap, msg);
109     // can not use ctx.debug() <<... because QT_NO_DEBUG_STREAM maybe defined
110     log_helper(QtDebugMsg, &ctx, msg, ap);
111     va_end(ap);
112 }
113 
warning(const char * msg,...) const114 void Logger::warning(const char *msg, ...) const
115 {
116     QtAVDebug d; // initialize something. e.g. environment check
117     Q_UNUSED(d);
118     const int v = (int)logLevel();
119     if (v <= (int)LogOff)
120         return;
121     if (v > (int)LogWarning && v < (int)LogAll)
122         return;
123     va_list ap;
124     va_start(ap, msg);
125     log_helper(QtWarningMsg, &ctx, msg, ap);
126     va_end(ap);
127 }
128 
critical(const char * msg,...) const129 void Logger::critical(const char *msg, ...) const
130 {
131     QtAVDebug d; // initialize something. e.g. environment check
132     Q_UNUSED(d);
133     const int v = (int)logLevel();
134     if (v <= (int)LogOff)
135         return;
136     if (v > (int)LogCritical && v < (int)LogAll)
137         return;
138     va_list ap;
139     va_start(ap, msg);
140     log_helper(QtCriticalMsg, &ctx, msg, ap);
141     va_end(ap);
142 }
143 
fatal(const char * msg,...) const144 void Logger::fatal(const char *msg, ...) const Q_DECL_NOTHROW
145 {
146     QtAVDebug d; // initialize something. e.g. environment check
147     Q_UNUSED(d);
148     const int v = (int)logLevel();
149     /*
150     if (v <= (int)LogOff)
151         abort();
152     if (v > (int)LogFatal && v < (int)LogAll)
153         abort();
154     */
155     if (v > (int)LogOff) {
156         va_list ap;
157         va_start(ap, msg);
158         log_helper(QtFatalMsg, &ctx, msg, ap);
159         va_end(ap);
160     }
161     abort();
162 }
163 
164 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
165 #ifndef QT_NO_DEBUG_STREAM
166 // internal used by Logger::fatal(const char*,...) with log level checked, so always do things here
fatal(const char * msg,...) const167 void Logger::Context::fatal(const char *msg, ...) const Q_DECL_NOTHROW
168 {
169     va_list ap;
170     va_start(ap, msg);
171     QString qmsg;
172     if (msg)
173         qmsg += QString().vsprintf(msg, ap);
174     qt_message_output(QtFatalMsg, qmsg.toUtf8().constData());
175     va_end(ap);
176     abort();
177 }
178 #endif //QT_NO_DEBUG_STREAM
179 #endif //QT_VERSION
180 
181 #ifndef QT_NO_DEBUG_STREAM
182 // will print message in ~QDebug()
183 // can not use QDebug on stack. It must lives in QtAVDebug
debug() const184 QtAVDebug Logger::debug() const
185 {
186     QtAVDebug d(QtDebugMsg); //// initialize something. e.g. environment check
187     const int v = (int)logLevel();
188     if (v <= (int)LogOff)
189         return d;
190     if (v <= (int)LogDebug || v >= (int)LogAll)
191         d.setQDebug(new QDebug(ctx.debug()));
192     return d; //ref > 0
193 }
194 
warning() const195 QtAVDebug Logger::warning() const
196 {
197     QtAVDebug d(QtWarningMsg);
198     const int v = (int)logLevel();
199     if (v <= (int)LogOff)
200         return d;
201     if (v <= (int)LogWarning || v >= (int)LogAll)
202         d.setQDebug(new QDebug(ctx.warning()));
203     return d;
204 }
205 
critical() const206 QtAVDebug Logger::critical() const
207 {
208     QtAVDebug d(QtCriticalMsg);
209     const int v = (int)logLevel();
210     if (v <= (int)LogOff)
211         return d;
212     if (v <= (int)LogCritical || v >= (int)LogAll)
213         d.setQDebug(new QDebug(ctx.critical()));
214     return d;
215 }
216 // no QMessageLogger::fatal()
217 #endif //QT_NO_DEBUG_STREAM
218 
219 bool isLogLevelSet();
220 void print_library_info();
221 
QtAVDebug(QtMsgType t,QDebug * d)222 QtAVDebug::QtAVDebug(QtMsgType t, QDebug *d)
223     : type(t)
224     , dbg(0)
225 {
226     if (d)
227         setQDebug(d); // call *dbg << gQtAVLogTag
228     static bool sFirstRun = true;
229     if (!sFirstRun)
230         return;
231     sFirstRun = false;
232     printf("%s\n", aboutQtAV_PlainText().toUtf8().constData());
233     // check environment var and call other functions at first Qt logging call
234     // always override setLogLevel()
235     QByteArray env = qgetenv("QTAV_LOG_LEVEL");
236     if (env.isEmpty())
237         env = qgetenv("QTAV_LOG");
238     if (!env.isEmpty()) {
239         bool ok = false;
240         const int level = env.toInt(&ok);
241         if (ok) {
242             if (level < (int)LogOff)
243                 setLogLevel(LogOff);
244             else if (level > (int)LogAll)
245                 setLogLevel(LogAll);
246             else
247                 setLogLevel((LogLevel)level);
248         } else {
249             env = env.toLower();
250             if (env.endsWith("off"))
251                 setLogLevel(LogOff);
252             else if (env.endsWith("debug"))
253                 setLogLevel(LogDebug);
254             else if (env.endsWith("warning"))
255                 setLogLevel(LogWarning);
256             else if (env.endsWith("critical"))
257                 setLogLevel(LogCritical);
258             else if (env.endsWith("fatal"))
259                 setLogLevel(LogFatal);
260             else if (env.endsWith("all") || env.endsWith("default"))
261                 setLogLevel(LogAll);
262         }
263     }
264     env = qgetenv("QTAV_LOG_TAG");
265     if (!env.isEmpty()) {
266         gQtAVLogTag = QString::fromUtf8(env);
267     }
268 
269     if ((int)logLevel() > (int)LogOff) {
270         print_library_info();
271     }
272 }
273 
~QtAVDebug()274 QtAVDebug::~QtAVDebug()
275 {
276 }
277 
setQDebug(QDebug * d)278 void QtAVDebug::setQDebug(QDebug *d)
279 {
280     dbg = QSharedPointer<QDebug>(d);
281     if (dbg && !gQtAVLogTag.isEmpty()) {
282         *dbg << gQtAVLogTag;
283     }
284 }
285 
286 #if 0
287 QtAVDebug debug(const char *msg, ...)
288 {
289     if ((int)logLevel() > (int)Debug && logLevel() != All) {
290         return QtAVDebug();
291     }
292     va_list ap;
293     va_start(ap, msg); // use variable arg list
294     QMessageLogContext ctx;
295     log_helper(QtDebugMsg, &ctx, msg, ap);
296     va_end(ap);
297     return QtAVDebug();
298 }
299 #endif
300 
301 } //namespace Internal
302 } // namespace QtAV
303 
304 #endif //QTAV_NO_LOG_LEVEL
305