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 
31 namespace 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  */
50 LogView::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  */
60 LogView::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. */
79 void
80 LogView::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  */
148 void
149 LogView::set_client(FawkesNetworkClient *client)
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()) {
159  FawkesNetworkMessage *msg =
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  */
168 FawkesNetworkClient *
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. */
184 void
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  */
194 void
195 LogView::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)
215 bool
216 LogView::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
222 void
223 LogView::on_expose_notify(GdkEventExpose *event)
224 {
225  have_recently_added_path_ = false;
226 }
227 #endif
228 
229 void
230 LogView::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 
243 void
244 LogView::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  */
258 void
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  */
327 void
328 LogView::on_message_received(FawkesNetworkMessage *msg)
329 {
330  if ((msg->cid() == FAWKES_CID_NETWORKLOGGER)
331  && (msg->msgid() == NetworkLogger::MSGTYPE_LOGMESSAGE)) {
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. */
349 LogView::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
fawkes::NetworkLoggerMessageContent
Definition: network_logger.h:126
fawkes::ConnectionDispatcher::signal_message_received
sigc::signal< void, FawkesNetworkMessage * > signal_message_received()
Get "message received" signal.
Definition: connection_dispatcher.cpp:210
fawkes::FawkesNetworkClient::enqueue
void enqueue(FawkesNetworkMessage *message)
Enqueue message to send.
Definition: client.cpp:600
fawkes::FawkesNetworkMessage::cid
unsigned short int cid() const
Get component ID.
Definition: message.cpp:289
fawkes::LogView::append_message
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:263
fawkes::ConnectionDispatcher::set_client
void set_client(FawkesNetworkClient *client)
Set Fawkes network client.
Definition: connection_dispatcher.cpp:113
fawkes::FawkesNetworkMessage::msgc
MT * msgc() const
Get correctly parsed output.
Definition: message.h:163
fawkes::NetworkLoggerMessageContent::get_time
struct timeval get_time() const
Get time.
Definition: network_logger.cpp:527
fawkes::LogView::get_client
FawkesNetworkClient * get_client()
Get the used FawkesNetworkClient.
Definition: logview.cpp:173
fawkes::NetworkLogger::MSGTYPE_SUBSCRIBE
Subscribe for logging messages.
Definition: network_logger.h:88
fawkes::Logger::LL_DEBUG
debug output, relevant only when tracking down problems
Definition: logger.h:51
fawkes::ConnectionDispatcher::signal_connected
sigc::signal< void > signal_connected()
Get "connected" signal.
Definition: connection_dispatcher.cpp:220
fawkes::NetworkLogger::MSGTYPE_UNSUBSCRIBE
Unsubscribe from receiving logging messages.
Definition: network_logger.h:89
fawkes::Logger::LL_INFO
informational output about normal procedures
Definition: logger.h:52
fawkes::NetworkLoggerMessageContent::is_exception
bool is_exception() const
Check if message was generated by exception.
Definition: network_logger.cpp:566
fawkes::ConnectionDispatcher
Definition: connection_dispatcher.h:42
fawkes::NetworkLoggerMessageContent::get_loglevel
Logger::LogLevel get_loglevel() const
Log level.
Definition: network_logger.cpp:557
fawkes
fawkes::NetworkLogger::MSGTYPE_LOGMESSAGE
Log message.
Definition: network_logger.h:90
fawkes::Logger::LL_ERROR
error, may be recoverable (software still running) or not (software has to terminate).
Definition: logger.h:56
fawkes::ConnectionDispatcher::signal_disconnected
sigc::signal< void > signal_disconnected()
Get "disconnected" signal.
Definition: connection_dispatcher.cpp:231
fawkes::LogView::set_client
void set_client(FawkesNetworkClient *client)
Set FawkesNetworkClient instance.
Definition: logview.cpp:153
fawkes::Logger::LogLevel
LogLevel
Log level.
Definition: logger.h:50
fawkes::FawkesNetworkMessage
Definition: message.h:80
fawkes::LogView::~LogView
~LogView()
Destructor.
Definition: logview.cpp:71
fawkes::Logger::LL_WARN
warning, should be investigated but software still functions, an example is that something was reques...
Definition: logger.h:53
fawkes::LogView::clear
void clear()
Clear all records.
Definition: logview.cpp:189
fawkes::FawkesNetworkClient::connected
bool connected() const
Check if connection is alive.
Definition: client.cpp:832
fawkes::FawkesNetworkClient
Definition: client.h:55
fawkes::NetworkLoggerMessageContent::get_component
const char * get_component() const
Get component.
Definition: network_logger.cpp:539
fawkes::ConnectionDispatcher::get_client
FawkesNetworkClient * get_client()
Get client.
Definition: connection_dispatcher.cpp:130
fawkes::NetworkLoggerMessageContent::get_message
const char * get_message() const
Get message.
Definition: network_logger.cpp:548
fawkes::FawkesNetworkMessage::msgid
unsigned short int msgid() const
Get message type ID.
Definition: message.cpp:298
fawkes::LogView::get_connection_dispatcher
ConnectionDispatcher * get_connection_dispatcher() const
Get ConnectionDispatcher instance that is used internally.
Definition: logview.cpp:182
fawkes::LogView::LogView
LogView()
Constructor.
Definition: logview.cpp:45