Fawkes API Fawkes Development Version
logview.cpp
1
2/***************************************************************************
3 * logview.cpp - Fawkes log view widget
4 *
5 * Created: Mon Nov 02 13:19:03 2008
6 * Copyright 2008 Tim Niemueller [www.niemueller.de]
7 *
8 ****************************************************************************/
9
10/* This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version. A runtime exception applies to
14 * this software (see LICENSE.GPL_WRE file mentioned below for details).
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Library General Public License for more details.
20 *
21 * Read the full text in the LICENSE.GPL_WRE file in the doc directory.
22 */
23
24#include <gui_utils/connection_dispatcher.h>
25#include <gui_utils/logview.h>
26#include <netcomm/fawkes/client.h>
27#include <network_logger/network_logger.h>
28
29#include <gtkmm.h>
30
31namespace fawkes {
32
33/** @class LogView <gui_utils/logview.h>
34 * Log View widget.
35 * This widget derives a Gtk::TreeView and provides an easy way to show
36 * log messages in a GUI application.
37 * @author Tim Niemueller
38 */
39
40/** Constructor. */
42{
43 ctor();
44}
45
46/** Constructor.
47 * @param hostname hostname to set for the FawkesNetworkClient.
48 * @param port port to set for the FawkesNetworkClient.
49 */
50LogView::LogView(const char *hostname, unsigned short int port)
51{
52 ctor(hostname, port);
53}
54
55/** Constructor.
56 * Special ctor to be used with Gtk::Builder's get_widget_derived().
57 * @param cobject Gtk C object
58 * @param builder Gtk builder
59 */
60LogView::LogView(BaseObjectType *cobject, const Glib::RefPtr<Gtk::Builder> &builder)
61: Gtk::TreeView(cobject)
62{
63 ctor();
64}
65
66/** Destructor. */
68{
69 FawkesNetworkClient *c = connection_dispatcher_->get_client();
70 if (c && c->connected()) {
72 new FawkesNetworkMessage(FAWKES_CID_NETWORKLOGGER, NetworkLogger::MSGTYPE_UNSUBSCRIBE);
73 c->enqueue(msg);
74 }
75 delete connection_dispatcher_;
76}
77
78/** Internal constructor method. */
79void
80LogView::ctor(const char *hostname, unsigned short int port)
81{
82 list_ = Gtk::ListStore::create(record_);
83 set_model(list_);
84 have_recently_added_path_ = false;
85
86 list_->signal_row_inserted().connect(sigc::mem_fun(*this, &LogView::on_row_inserted));
87 get_selection()->set_mode(Gtk::SELECTION_NONE);
88
89 if ((hostname != NULL) && (port != 0)) {
90 connection_dispatcher_ = new ConnectionDispatcher(hostname, port, FAWKES_CID_NETWORKLOGGER);
91 } else {
92 connection_dispatcher_ = new ConnectionDispatcher(FAWKES_CID_NETWORKLOGGER);
93 }
94
95 append_column("Level", record_.loglevel);
96 append_column("Time", record_.time);
97 int compcol = append_column("Component", record_.component);
98 int msgcol = append_column("Message", record_.message);
99
100 // We stored the number of columns, for an index (which starts at 0) we need
101 // to subtract 1
102 compcol -= 1;
103 msgcol -= 1;
104
105 Glib::ListHandle<Gtk::TreeViewColumn *> columns = get_columns();
106 int colnum = -1;
107 for (Glib::ListHandle<Gtk::TreeViewColumn *>::iterator c = columns.begin(); c != columns.end();
108 ++c) {
109 ++colnum;
110#if GTK_VERSION_GE(3, 0)
111 Gtk::CellRenderer *cell_renderer = (*c)->get_first_cell();
112#else
113 Gtk::CellRenderer *cell_renderer = (*c)->get_first_cell_renderer();
114#endif
115 Gtk::CellRendererText *text_renderer = dynamic_cast<Gtk::CellRendererText *>(cell_renderer);
116 if (text_renderer) {
117#ifdef GLIBMM_PROPERTIES_ENABLED
118 if ((colnum == compcol) || (colnum == msgcol)) {
119 (*c)->set_resizable();
120 }
121 if (colnum == compcol) {
122 text_renderer->property_ellipsize().set_value(Pango::ELLIPSIZE_END);
123 }
124
125 (*c)->add_attribute(text_renderer->property_background_gdk(), record_.background);
126 (*c)->add_attribute(text_renderer->property_foreground_gdk(), record_.foreground);
127#else
128 (*c)->add_attribute(*text_renderer, "background-gdk", record_.background);
129 (*c)->add_attribute(*text_renderer, "foreground-gdk", record_.background);
130#endif
131 }
132 }
133
134 connection_dispatcher_->signal_message_received().connect(
135 sigc::mem_fun(*this, &LogView::on_message_received));
136 connection_dispatcher_->signal_connected().connect(
137 sigc::mem_fun(*this, &LogView::on_client_connected));
138 connection_dispatcher_->signal_disconnected().connect(
139 sigc::mem_fun(*this, &LogView::on_client_disconnected));
140#if GTK_VERSION_LT(3, 0)
141 signal_expose_event().connect_notify(sigc::mem_fun(*this, &LogView::on_expose_notify));
142#endif
143}
144
145/** Set FawkesNetworkClient instance.
146 * @param client Fawkes network client to use
147 */
148void
150{
151 FawkesNetworkClient *c = connection_dispatcher_->get_client();
152 if (c && c->connected()) {
154 new FawkesNetworkMessage(FAWKES_CID_NETWORKLOGGER, NetworkLogger::MSGTYPE_UNSUBSCRIBE);
155 c->enqueue(msg);
156 }
157 connection_dispatcher_->set_client(client);
158 if (client && client->connected()) {
160 new FawkesNetworkMessage(FAWKES_CID_NETWORKLOGGER, NetworkLogger::MSGTYPE_SUBSCRIBE);
161 client->enqueue(msg);
162 }
163}
164
165/** Get the used FawkesNetworkClient.
166 * @return Fawkes network client instance
167 */
170{
171 return connection_dispatcher_->get_client();
172}
173
174/** Get ConnectionDispatcher instance that is used internally.
175 * @return connection dispatcher
176 */
179{
180 return connection_dispatcher_;
181}
182
183/** Clear all records. */
184void
186{
187 list_->clear();
188}
189
190/** Event handler when row inserted.
191 * @param path path to element
192 * @param iter iterator to inserted element
193 */
194void
195LogView::on_row_inserted(const Gtk::TreeModel::Path &path, const Gtk::TreeModel::iterator &iter)
196{
197 Gtk::TreeModel::Path vstart, vend;
198 Gtk::TreeModel::Path prev = path;
199 get_visible_range(vstart, vend);
200 prev = path;
201 if (!get_visible_range(vstart, vend)
202 || (prev.prev()
203 && ((vend == prev) || (have_recently_added_path_ && (recently_added_path_ == prev))))) {
204 scroll_to_row(path);
205
206 // the recently added stuff is required if multiple rows are inserted at
207 // a time. In this case the widget wasn't redrawn and get_visible_range() does
208 // not give the desired result and we have to "advance" it manually
209 have_recently_added_path_ = true;
210 recently_added_path_ = path;
211 }
212}
213
214#if GTK_VERSION_GE(3, 0)
215bool
216LogView::on_draw(const Cairo::RefPtr<Cairo::Context> &cr)
217{
218 have_recently_added_path_ = false;
219 return Gtk::TreeView::on_draw(cr);
220}
221#else
222void
223LogView::on_expose_notify(GdkEventExpose *event)
224{
225 have_recently_added_path_ = false;
226}
227#endif
228
229void
230LogView::on_client_connected()
231{
232 FawkesNetworkClient *c = connection_dispatcher_->get_client();
233 if (c && c->connected()) {
234 FawkesNetworkMessage *msg =
235 new FawkesNetworkMessage(FAWKES_CID_NETWORKLOGGER, NetworkLogger::MSGTYPE_SUBSCRIBE);
236 c->enqueue(msg);
237 struct timeval t;
238 gettimeofday(&t, NULL);
239 append_message(Logger::LL_DEBUG, t, "LogView", false, "Connected");
240 }
241}
242
243void
244LogView::on_client_disconnected()
245{
246 struct timeval t;
247 gettimeofday(&t, NULL);
248 append_message(Logger::LL_ERROR, t, "LogView", false, "*** Connection died. ***");
249}
250
251/** Append a single message.
252 * @param log_level log level
253 * @param t time of the message
254 * @param component component string for the message
255 * @param is_exception true if essage was produced via an exception
256 * @param message log message
257 */
258void
260 struct timeval t,
261 const char * component,
262 bool is_exception,
263 const char * message)
264{
265 const char *loglevel;
266 const char *timestr;
267 char * time = NULL;
268 Gdk::Color color;
269 bool set_foreground = false;
270 bool set_background = false;
271
272 switch (log_level) {
273 case Logger::LL_DEBUG:
274 loglevel = "DEBUG";
275 color.set_rgb_p(0.4, 0.4, 0.4);
276 set_foreground = true;
277 break;
278 case Logger::LL_INFO: loglevel = "INFO"; break;
279 case Logger::LL_WARN:
280 loglevel = "WARN";
281 color.set_rgb_p(1.0, 1.0, 0.7);
282 set_background = true;
283 break;
284 case Logger::LL_ERROR:
285 loglevel = "ERROR";
286 color.set_rgb_p(1.0, 0.8, 0.8);
287 set_background = true;
288 break;
289 default:
290 loglevel = "NONE?";
291 color.set_rgb_p(1.0, 0.0, 0.0);
292 set_background = true;
293 break;
294 }
295
296 struct tm time_tm;
297 localtime_r(&(t.tv_sec), &time_tm);
298 if (asprintf(
299 &time, "%02d:%02d:%02d.%06ld", time_tm.tm_hour, time_tm.tm_min, time_tm.tm_sec, t.tv_usec)
300 == -1) {
301 timestr = "OutOfMemory";
302 } else {
303 timestr = time;
304 }
305
306 Gtk::TreeModel::Row row = *list_->append();
307 row[record_.loglevel] = loglevel;
308 row[record_.time] = timestr;
309 row[record_.component] = component;
310 if (is_exception) {
311 row[record_.message] = std::string("[EXCEPTION] ") + message;
312 } else {
313 row[record_.message] = message;
314 }
315 if (set_foreground)
316 row[record_.foreground] = color;
317 if (set_background)
318 row[record_.background] = color;
319
320 if (time)
321 free(time);
322}
323
324/** Message received event handler.
325 * @param msg Fawkes network message just recveived.
326 */
327void
328LogView::on_message_received(FawkesNetworkMessage *msg)
329{
330 if ((msg->cid() == FAWKES_CID_NETWORKLOGGER)
333
334 append_message(content->get_loglevel(),
335 content->get_time(),
336 content->get_component(),
337 content->is_exception(),
338 content->get_message());
339
340 delete content;
341 }
342}
343
344/** @class LogView::LogRecord <gui_utils/logview.h>
345 * TreeView record for LogView.
346 */
347
348/** Constructor. */
349LogView::LogRecord::LogRecord()
350{
351 add(loglevel);
352 add(time);
353 add(component);
354 add(message);
355 add(foreground);
356 add(background);
357}
358
359} // end namespace fawkes
Watches network client events and dispatches them as signals.
void set_client(FawkesNetworkClient *client)
Set Fawkes network client.
sigc::signal< void > signal_connected()
Get "connected" signal.
FawkesNetworkClient * get_client()
Get client.
sigc::signal< void, FawkesNetworkMessage * > signal_message_received()
Get "message received" signal.
sigc::signal< void > signal_disconnected()
Get "disconnected" signal.
Simple Fawkes network client.
Definition: client.h:52
bool connected() const noexcept
Check if connection is alive.
Definition: client.cpp:828
void enqueue(FawkesNetworkMessage *message)
Enqueue message to send.
Definition: client.cpp:596
Representation of a message that is sent over the network.
Definition: message.h:77
unsigned short int msgid() const
Get message type ID.
Definition: message.cpp:294
unsigned short int cid() const
Get component ID.
Definition: message.cpp:285
MT * msgc() const
Get correctly parsed output.
Definition: message.h:159
void append_message(Logger::LogLevel log_level, struct timeval t, const char *component, bool is_exception, const char *message)
Append a single message.
Definition: logview.cpp:259
void clear()
Clear all records.
Definition: logview.cpp:185
ConnectionDispatcher * get_connection_dispatcher() const
Get ConnectionDispatcher instance that is used internally.
Definition: logview.cpp:178
FawkesNetworkClient * get_client()
Get the used FawkesNetworkClient.
Definition: logview.cpp:169
~LogView()
Destructor.
Definition: logview.cpp:67
void set_client(FawkesNetworkClient *client)
Set FawkesNetworkClient instance.
Definition: logview.cpp:149
LogView()
Constructor.
Definition: logview.cpp:41
LogLevel
Log level.
Definition: logger.h:51
@ LL_INFO
informational output about normal procedures
Definition: logger.h:53
@ LL_WARN
warning, should be investigated but software still functions, an example is that something was reques...
Definition: logger.h:54
@ LL_ERROR
error, may be recoverable (software still running) or not (software has to terminate).
Definition: logger.h:57
@ LL_DEBUG
debug output, relevant only when tracking down problems
Definition: logger.h:52
Message sent over the network with a log message.
Logger::LogLevel get_loglevel() const
Log level.
struct timeval get_time() const
Get time.
const char * get_message() const
Get message.
const char * get_component() const
Get component.
bool is_exception() const
Check if message was generated by exception.
@ MSGTYPE_SUBSCRIBE
Subscribe for logging messages.
@ MSGTYPE_LOGMESSAGE
Log message.
@ MSGTYPE_UNSUBSCRIBE
Unsubscribe from receiving logging messages.
Fawkes library namespace.