1 /*
2 * Inkscape::Debug::Logger - debug logging facility
3 *
4 * Authors:
5 * MenTaLguY <mental@rydia.net>
6 *
7 * Copyright (C) 2005 MenTaLguY
8 *
9 * Released under GNU GPL, read the file 'COPYING' for more information
10 */
12 #include <fstream>
13 #include <vector>
14 #include <glib/gmessages.h>
15 #include "inkscape-version.h"
16 #include "debug/logger.h"
17 #include "debug/simple-event.h"
18 #include "gc-alloc.h"
20 namespace Inkscape {
22 namespace Debug {
24 bool Logger::_enabled=false;
25 bool Logger::_category_mask[Event::N_CATEGORIES];
27 namespace {
29 static void write_escaped_value(std::ostream &os, Util::ptr_shared<char> value) {
30 for ( char const *current=value ; *current ; ++current ) {
31 switch (*current) {
32 case '&':
33 os << "&";
34 break;
35 case '"':
36 os << """;
37 break;
38 case '\'':
39 os << "'";
40 break;
41 case '<':
42 os << "<";
43 break;
44 case '>':
45 os << ">";
46 break;
47 default:
48 os.put(*current);
49 }
50 }
51 }
53 static void write_indent(std::ostream &os, unsigned depth) {
54 for ( unsigned i = 0 ; i < depth ; i++ ) {
55 os.write(" ", 2);
56 }
57 }
59 static std::ofstream log_stream;
60 static bool empty_tag=false;
61 typedef std::vector<Util::ptr_shared<char>, GC::Alloc<Util::ptr_shared<char>, GC::MANUAL> > TagStack;
62 static TagStack &tag_stack() {
63 static TagStack stack;
64 return stack;
65 }
67 static void do_shutdown() {
68 Debug::Logger::shutdown();
69 }
71 static bool equal_range(char const *c_string,
72 char const *start, char const *end)
73 {
74 return !std::strncmp(start, c_string, end - start) &&
75 !c_string[end - start];
76 }
78 static void set_category_mask(bool * const mask, char const *filter) {
79 if (!filter) {
80 for ( unsigned i = 0 ; i < Event::N_CATEGORIES ; i++ ) {
81 mask[i] = true;
82 }
83 return;
84 } else {
85 for ( unsigned i = 0 ; i < Event::N_CATEGORIES ; i++ ) {
86 mask[i] = false;
87 }
88 mask[Event::CORE] = true;
89 }
91 char const *start;
92 char const *end;
93 start = end = filter;
94 while (*end) {
95 while ( *end && *end != ',' ) { end++; }
96 if ( start != end ) {
97 struct CategoryName {
98 char const *name;
99 Event::Category category;
100 };
101 static const CategoryName category_names[] = {
102 { "CORE", Event::CORE },
103 { "XML", Event::XML },
104 { "SPOBJECT", Event::SPOBJECT },
105 { "DOCUMENT", Event::DOCUMENT },
106 { "REFCOUNT", Event::REFCOUNT },
107 { "EXTENSION", Event::EXTENSION },
108 { "FINALIZERS", Event::FINALIZERS },
109 { "INTERACTION", Event::INTERACTION },
110 { "CONFIGURATION", Event::CONFIGURATION },
111 { "OTHER", Event::OTHER },
112 { NULL, Event::OTHER }
113 };
114 CategoryName const *iter;
115 for ( iter = category_names ; iter->name ; iter++ ) {
116 if (equal_range(iter->name, start, end)) {
117 mask[iter->category] = true;
118 break;
119 }
120 }
121 if (!iter->name) {
122 g_warning("Unknown debugging category %*s", (int)(end - start), start);
123 }
124 }
125 if (*end) {
126 start = end = end + 1;
127 }
128 }
129 }
131 typedef SimpleEvent<Event::CORE> CoreEvent;
133 class SessionEvent : public CoreEvent {
134 public:
135 SessionEvent() : CoreEvent(Util::share_static_string("session")) {
136 _addProperty("inkscape-version", Inkscape::version_string);
137 }
138 };
140 }
142 void Logger::init() {
143 if (!_enabled) {
144 char const *log_filename=std::getenv("INKSCAPE_DEBUG_LOG");
145 if (log_filename) {
146 log_stream.open(log_filename);
147 if (log_stream.is_open()) {
148 char const *log_filter=std::getenv("INKSCAPE_DEBUG_FILTER");
149 set_category_mask(_category_mask, log_filter);
150 log_stream << "<?xml version=\"1.0\"?>\n";
151 log_stream.flush();
152 _enabled = true;
153 start<SessionEvent>();
154 std::atexit(&do_shutdown);
155 }
156 }
157 }
158 }
160 void Logger::_start(Event const &event) {
161 Util::ptr_shared<char> name=event.name();
163 if (empty_tag) {
164 log_stream << ">\n";
165 }
167 write_indent(log_stream, tag_stack().size());
169 log_stream << "<" << name.pointer();
171 unsigned property_count=event.propertyCount();
172 for ( unsigned i = 0 ; i < property_count ; i++ ) {
173 Event::PropertyPair property=event.property(i);
174 log_stream << " " << property.name.pointer() << "=\"";
175 write_escaped_value(log_stream, property.value);
176 log_stream << "\"";
177 }
179 log_stream.flush();
181 tag_stack().push_back(name);
182 empty_tag = true;
184 event.generateChildEvents();
185 }
187 void Logger::_skip() {
188 tag_stack().push_back(Util::ptr_shared<char>());
189 }
191 void Logger::_finish() {
192 if (tag_stack().back()) {
193 if (empty_tag) {
194 log_stream << "/>\n";
195 } else {
196 write_indent(log_stream, tag_stack().size() - 1);
197 log_stream << "</" << tag_stack().back().pointer() << ">\n";
198 }
199 log_stream.flush();
201 empty_tag = false;
202 }
204 tag_stack().pop_back();
205 }
207 void Logger::shutdown() {
208 if (_enabled) {
209 while (!tag_stack().empty()) {
210 finish();
211 }
212 }
213 }
215 }
217 }
219 /*
220 Local Variables:
221 mode:c++
222 c-file-style:"stroustrup"
223 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
224 indent-tabs-mode:nil
225 fill-column:99
226 End:
227 */
228 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :