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