1 module soup.Logger; 2 3 private import glib.ConstructionException; 4 private import gobject.ObjectG; 5 private import soup.Session; 6 private import soup.SessionFeatureIF; 7 private import soup.SessionFeatureT; 8 private import soup.c.functions; 9 public import soup.c.types; 10 11 12 /** 13 * #SoupLogger watches a #SoupSession and logs the HTTP traffic that 14 * it generates, for debugging purposes. Many applications use an 15 * environment variable to determine whether or not to use 16 * #SoupLogger, and to determine the amount of debugging output. 17 * 18 * To use #SoupLogger, first create a logger with soup_logger_new(), 19 * optionally configure it with soup_logger_set_request_filter(), 20 * soup_logger_set_response_filter(), and soup_logger_set_printer(), 21 * and then attach it to a session (or multiple sessions) with 22 * soup_session_add_feature(). 23 * 24 * By default, the debugging output is sent to 25 * <literal>stdout</literal>, and looks something like: 26 * 27 * <informalexample><screen> 28 * > POST /unauth HTTP/1.1 29 * > Soup-Debug-Timestamp: 1200171744 30 * > Soup-Debug: SoupSessionAsync 1 (0x612190), SoupMessage 1 (0x617000), SoupSocket 1 (0x612220) 31 * > Host: localhost 32 * > Content-Type: text/plain 33 * > Connection: close 34 * > 35 * > This is a test. 36 * 37 * < HTTP/1.1 201 Created 38 * < Soup-Debug-Timestamp: 1200171744 39 * < Soup-Debug: SoupMessage 1 (0x617000) 40 * < Date: Sun, 12 Jan 2008 21:02:24 GMT 41 * < Content-Length: 0 42 * </screen></informalexample> 43 * 44 * The <literal>Soup-Debug-Timestamp</literal> line gives the time (as 45 * a <type>time_t</type>) when the request was sent, or the response fully 46 * received. 47 * 48 * The <literal>Soup-Debug</literal> line gives further debugging 49 * information about the #SoupSession, #SoupMessage, and #SoupSocket 50 * involved; the hex numbers are the addresses of the objects in 51 * question (which may be useful if you are running in a debugger). 52 * The decimal IDs are simply counters that uniquely identify objects 53 * across the lifetime of the #SoupLogger. In particular, this can be 54 * used to identify when multiple messages are sent across the same 55 * connection. 56 * 57 * Currently, the request half of the message is logged just before 58 * the first byte of the request gets written to the network (from the 59 * #SoupMessage::starting signal), which means that if you have 60 * not made the complete request body available at that point, it will 61 * not be logged. 62 * 63 * The response is logged just after the last byte of the response 64 * body is read from the network (from the #SoupMessage::got_body or 65 * #SoupMessage::got_informational signal), which means that the 66 * #SoupMessage::got_headers signal, and anything triggered off it 67 * (such as #SoupSession::authenticate) will be emitted 68 * <emphasis>before</emphasis> the response headers are actually 69 * logged. 70 * 71 * If the response doesn't happen to trigger the 72 * #SoupMessage::got_body nor #SoupMessage::got_informational signals 73 * due to, for example, a cancellation before receiving the last byte 74 * of the response body, the response will still be logged on the 75 * event of the #SoupMessage::finished signal. 76 */ 77 public class Logger : ObjectG, SessionFeatureIF 78 { 79 /** the main Gtk struct */ 80 protected SoupLogger* soupLogger; 81 82 /** Get the main Gtk struct */ 83 public SoupLogger* getLoggerStruct(bool transferOwnership = false) 84 { 85 if (transferOwnership) 86 ownedRef = false; 87 return soupLogger; 88 } 89 90 /** the main Gtk struct as a void* */ 91 protected override void* getStruct() 92 { 93 return cast(void*)soupLogger; 94 } 95 96 /** 97 * Sets our main struct and passes it to the parent class. 98 */ 99 public this (SoupLogger* soupLogger, bool ownedRef = false) 100 { 101 this.soupLogger = soupLogger; 102 super(cast(GObject*)soupLogger, ownedRef); 103 } 104 105 // add the SessionFeature capabilities 106 mixin SessionFeatureT!(SoupLogger); 107 108 109 /** */ 110 public static GType getType() 111 { 112 return soup_logger_get_type(); 113 } 114 115 /** 116 * Creates a new #SoupLogger with the given debug level. If @level is 117 * %SOUP_LOGGER_LOG_BODY, @max_body_size gives the maximum number of 118 * bytes of the body that will be logged. (-1 means "no limit".) 119 * 120 * If you need finer control over what message parts are and aren't 121 * logged, use soup_logger_set_request_filter() and 122 * soup_logger_set_response_filter(). 123 * 124 * Params: 125 * level = the debug level 126 * maxBodySize = the maximum body size to output, or -1 127 * 128 * Returns: a new #SoupLogger 129 * 130 * Throws: ConstructionException GTK+ fails to create the object. 131 */ 132 public this(SoupLoggerLogLevel level, int maxBodySize) 133 { 134 auto __p = soup_logger_new(level, maxBodySize); 135 136 if(__p is null) 137 { 138 throw new ConstructionException("null returned by new"); 139 } 140 141 this(cast(SoupLogger*) __p, true); 142 } 143 144 /** 145 * Sets @logger to watch @session and print debug information for 146 * its messages. 147 * 148 * (The session will take a reference on @logger, which will be 149 * removed when you call soup_logger_detach(), or when the session is 150 * destroyed.) 151 * 152 * Deprecated: Use soup_session_add_feature() instead. 153 * 154 * Params: 155 * session = a #SoupSession 156 */ 157 public void attach(Session session) 158 { 159 soup_logger_attach(soupLogger, (session is null) ? null : session.getSessionStruct()); 160 } 161 162 /** 163 * Stops @logger from watching @session. 164 * 165 * Deprecated: Use soup_session_remove_feature() instead. 166 * 167 * Params: 168 * session = a #SoupSession 169 */ 170 public void detach(Session session) 171 { 172 soup_logger_detach(soupLogger, (session is null) ? null : session.getSessionStruct()); 173 } 174 175 /** 176 * Sets up an alternate log printing routine, if you don't want 177 * the log to go to <literal>stdout</literal>. 178 * 179 * Params: 180 * printer = the callback for printing logging output 181 * printerData = data to pass to the callback 182 * destroy = a #GDestroyNotify to free @printer_data 183 */ 184 public void setPrinter(SoupLoggerPrinter printer, void* printerData, GDestroyNotify destroy) 185 { 186 soup_logger_set_printer(soupLogger, printer, printerData, destroy); 187 } 188 189 /** 190 * Sets up a filter to determine the log level for a given request. 191 * For each HTTP request @logger will invoke @request_filter to 192 * determine how much (if any) of that request to log. (If you do not 193 * set a request filter, @logger will just always log requests at the 194 * level passed to soup_logger_new().) 195 * 196 * Params: 197 * requestFilter = the callback for request debugging 198 * filterData = data to pass to the callback 199 * destroy = a #GDestroyNotify to free @filter_data 200 */ 201 public void setRequestFilter(SoupLoggerFilter requestFilter, void* filterData, GDestroyNotify destroy) 202 { 203 soup_logger_set_request_filter(soupLogger, requestFilter, filterData, destroy); 204 } 205 206 /** 207 * Sets up a filter to determine the log level for a given response. 208 * For each HTTP response @logger will invoke @response_filter to 209 * determine how much (if any) of that response to log. (If you do not 210 * set a response filter, @logger will just always log responses at 211 * the level passed to soup_logger_new().) 212 * 213 * Params: 214 * responseFilter = the callback for response debugging 215 * filterData = data to pass to the callback 216 * destroy = a #GDestroyNotify to free @filter_data 217 */ 218 public void setResponseFilter(SoupLoggerFilter responseFilter, void* filterData, GDestroyNotify destroy) 219 { 220 soup_logger_set_response_filter(soupLogger, responseFilter, filterData, destroy); 221 } 222 }