1 /*
2  * Copyright (C) 2009, 2010 Hermann Meyer, James Warden, Andreas Degert
3  * Copyright (C) 2011 Pete Shorthose
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  * ---------------------------------------------------------------------------
19  */
20 
21 #include "gx_logging.h"
22 #include <iostream>
23 #include <iomanip>
24 
25 /****************************************************************
26  ** Logging
27  */
28 
GxLogger()29 GxLogger::GxLogger()
30     : trackable(),
31       msglist(),
32       msgmutex(),
33       got_new_msg(),
34       ui_thread(),
35       handlers(),
36       queue_all_msgs(true) {
37 }
38 
39 static class GxLoggerGuard {
40 private:
41     GxLogger *logger_instance;
42 public:
GxLoggerGuard()43     GxLoggerGuard() : logger_instance(0) {}
~GxLoggerGuard()44     ~GxLoggerGuard() { destroy(); }
create()45     void create() { logger_instance = new GxLogger; }
destroy()46     void destroy() { if (logger_instance) { delete logger_instance; logger_instance = 0; }}
get()47     GxLogger *get() { return logger_instance; }
48 } logger_guard;
49 
get_logger()50 GxLogger& GxLogger::get_logger() {
51     GxLogger *l = logger_guard.get();
52     if (!l) {
53 	logger_guard.create();
54 	l = logger_guard.get();
55     }
56     return *l;
57 }
58 
destroy()59 void GxLogger::destroy() {
60     logger_guard.destroy();
61 }
62 
~GxLogger()63 GxLogger::~GxLogger() {
64     delete got_new_msg;
65 }
66 
set_ui_thread()67 void GxLogger::set_ui_thread() {
68     if (ui_thread) {
69 	assert(pthread_equal(pthread_self(), ui_thread));
70     } else {
71 	got_new_msg = new Glib::Dispatcher;
72 	ui_thread = pthread_self();
73 	got_new_msg->connect(mem_fun(*this, &GxLogger::write_queued));
74     }
75 }
76 
signal_message()77 GxLogger::msg_signal& GxLogger::signal_message() {
78     set_ui_thread();
79     return handlers;
80 }
81 
unplug_queue()82 void GxLogger::unplug_queue() {
83     if (!queue_all_msgs) {
84 	return;
85     }
86     queue_all_msgs = false;
87     write_queued();
88 }
89 
write_queued()90 void GxLogger::write_queued() {
91     if (handlers.empty()) {
92 	return;
93     }
94 
95     // quick copy list
96     msgmutex.lock();
97     std::list<logmsg*> l = msglist;
98     if (!queue_all_msgs) {
99 	msglist.clear();
100     }
101     msgmutex.unlock();
102 
103     // feed throught the handler(s)
104     for (std::list<logmsg*>::iterator i = l.begin(); i != l.end(); ++i) {
105 	if (queue_all_msgs) {
106 	    if (!(*i)->plugged) {
107 		handlers((*i)->msg, (*i)->msgtype, (*i)->plugged);
108 		(*i)->plugged = true;
109 	    }
110 	} else {
111 	    handlers((*i)->msg, (*i)->msgtype, (*i)->plugged);
112             delete *i;
113 	}
114     }
115 }
116 
format(const char * func,const std::string & msg)117 std::string GxLogger::format(const char* func, const std::string& msg) {
118     // timestamp
119     time_t now;
120     time(&now);
121     struct tm *tm_now = localtime(&now);
122     std::ostringstream msgbuf;
123     msgbuf << "[" << std::setfill('0')
124            << std::setw(2) << tm_now->tm_hour << ":"
125            << std::setw(2) << tm_now->tm_min  << ":"
126            << std::setw(2) << tm_now->tm_sec  << "]"
127            << "  " << func << "  ***  " << msg;
128     return msgbuf.str();
129 }
130 
print(const char * func,const std::string & msg,GxLogger::MsgType msgtype)131 void GxLogger::print(const char* func, const std::string& msg, GxLogger::MsgType msgtype) {
132     GxLogger::print(format(func, msg), msgtype);
133 }
134 
print(const std::string & formatted_msg,GxLogger::MsgType msgtype)135 void GxLogger::print(const std::string& formatted_msg, GxLogger::MsgType msgtype) {
136     if (handlers.empty() || !(pthread_equal(pthread_self(), ui_thread))) {
137 	boost::mutex::scoped_lock lock(msgmutex);
138 	// defer output
139         msglist.push_back(new logmsg(formatted_msg, msgtype, false));
140 	if (!handlers.empty() && msglist.size() == 1) {
141 	    (*got_new_msg)();
142 	}
143     } else {
144 	write_queued();
145 	handlers(formatted_msg, msgtype, false);
146 	if (queue_all_msgs) {
147 	    msglist.push_back(new logmsg(formatted_msg, msgtype, true));
148 	}
149     }
150 }
151 
152 /*
153 ** utility logger functions
154 */
155 
156 // ---- log message handler
gx_print_logmsg(const char * func,const std::string & msg,GxLogger::MsgType msgtype)157 void gx_print_logmsg(const char* func, const std::string& msg, GxLogger::MsgType msgtype) {
158     GxLogger::get_logger().print(func, msg, msgtype);
159 }
160 
161 // warning
gx_print_warning(const char * func,const std::string & msg)162 void gx_print_warning(const char* func, const std::string& msg) {
163     gx_print_logmsg(func, msg, GxLogger::kWarning);
164 }
165 
166 // error
gx_print_error(const char * func,const std::string & msg)167 void gx_print_error(const char* func, const std::string& msg) {
168     gx_print_logmsg(func, msg, GxLogger::kError);
169 }
170 
~GxFatalError()171 GxFatalError::~GxFatalError() throw() {
172 }
173 
174 // fatal error
175 // - do not use before Gtk::Main() ctor
176 // - do not use when main loop is blocked (modal dialog or something)
177 //
gx_print_fatal(const char * func,const std::string & msg)178 void gx_print_fatal(const char* func, const std::string& msg) {
179     std::string msgbuf = std::string(_("fatal system error: ")) + func + "  ***  " + msg + "\n";
180     GxExit::get_instance().fatal_msg(msgbuf);
181 }
182 
183 // info
gx_print_info(const char * func,const std::string & msg)184 void gx_print_info(const char* func, const std::string& msg) {
185     gx_print_logmsg(func, msg, GxLogger::kInfo);
186 }
187 
188 
189 /****************************************************************
190  ** class GxExit
191  */
192 
GxExit()193 GxExit::GxExit(): exit_sig(), ui_thread() {}
194 
~GxExit()195 GxExit::~GxExit() {}
196 
exit_program(std::string msg,int errcode)197 void GxExit::exit_program(std::string msg, int errcode) {
198     exit_sig.emit_reverse(!pthread_equal(pthread_self(), ui_thread));
199     if (msg.empty()) {
200 	msg = "** guitarix exit **";
201     }
202     std::cerr << msg << std::endl;
203     _exit(errcode);
204 }
205 
get_instance()206 GxExit& GxExit::get_instance() {
207     static GxExit instance;
208     return instance;
209 }
210