log.d 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. /**
  2. A HTTP 1.1/1.0 server implementation.
  3. Copyright: © 2012-2013 Sönke Ludwig
  4. License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
  5. Authors: Sönke Ludwig, Jan Krüger
  6. */
  7. module vibe.http.log;
  8. import vibe.core.file;
  9. import vibe.core.log;
  10. import vibe.core.sync : InterruptibleTaskMutex, performLocked;
  11. import vibe.http.server;
  12. import vibe.container.internal.appender : FixedAppender;
  13. import std.array;
  14. import std.conv;
  15. import std.exception;
  16. import std.string;
  17. class HTTPLogger {
  18. @safe:
  19. private {
  20. string m_format;
  21. const(HTTPServerSettings) m_settings;
  22. InterruptibleTaskMutex m_mutex;
  23. Appender!(char[]) m_lineAppender;
  24. }
  25. this(const HTTPServerSettings settings, string format)
  26. {
  27. m_format = format;
  28. m_settings = settings;
  29. m_mutex = new InterruptibleTaskMutex;
  30. m_lineAppender.reserve(2048);
  31. }
  32. void close() {}
  33. final void log(scope HTTPServerRequest req, scope HTTPServerResponse res)
  34. {
  35. m_mutex.performLocked!(() @safe {
  36. m_lineAppender.clear();
  37. formatApacheLog(m_lineAppender, m_format, req, res, m_settings);
  38. writeLine(m_lineAppender.data);
  39. });
  40. }
  41. protected abstract void writeLine(const(char)[] ln);
  42. }
  43. final class HTTPConsoleLogger : HTTPLogger {
  44. @safe:
  45. this(HTTPServerSettings settings, string format)
  46. {
  47. super(settings, format);
  48. }
  49. protected override void writeLine(const(char)[] ln)
  50. {
  51. logInfo("%s", ln);
  52. }
  53. }
  54. final class HTTPFileLogger : HTTPLogger {
  55. @safe:
  56. private {
  57. FileStream m_stream;
  58. }
  59. this(HTTPServerSettings settings, string format, string filename)
  60. {
  61. m_stream = openFile(filename, FileMode.append);
  62. super(settings, format);
  63. }
  64. override void close()
  65. {
  66. m_stream.close();
  67. m_stream = FileStream.init;
  68. }
  69. protected override void writeLine(const(char)[] ln)
  70. {
  71. assert(!!m_stream);
  72. m_stream.write(ln);
  73. m_stream.write("\n");
  74. m_stream.flush();
  75. }
  76. }
  77. void formatApacheLog(R)(ref R ln, string format, scope HTTPServerRequest req, scope HTTPServerResponse res, in HTTPServerSettings settings)
  78. @safe {
  79. import std.format : formattedWrite;
  80. enum State {Init, Directive, Status, Key, Command}
  81. State state = State.Init;
  82. bool conditional = false;
  83. bool negate = false;
  84. bool match = false;
  85. string statusStr;
  86. string key = "";
  87. while( format.length > 0 ) {
  88. final switch(state) {
  89. case State.Init:
  90. auto idx = format.indexOf('%');
  91. if( idx < 0 ) {
  92. ln.put( format );
  93. format = "";
  94. } else {
  95. ln.put( format[0 .. idx] );
  96. format = format[idx+1 .. $];
  97. state = State.Directive;
  98. }
  99. break;
  100. case State.Directive:
  101. if( format[0] == '!' ) {
  102. conditional = true;
  103. negate = true;
  104. format = format[1 .. $];
  105. state = State.Status;
  106. } else if( format[0] == '%' ) {
  107. ln.put("%");
  108. format = format[1 .. $];
  109. state = State.Init;
  110. } else if( format[0] == '{' ) {
  111. format = format[1 .. $];
  112. state = State.Key;
  113. } else if( format[0] >= '0' && format[0] <= '9' ) {
  114. conditional = true;
  115. state = State.Status;
  116. } else {
  117. state = State.Command;
  118. }
  119. break;
  120. case State.Status:
  121. if( format[0] >= '0' && format[0] <= '9' ) {
  122. statusStr ~= format[0];
  123. format = format[1 .. $];
  124. } else if( format[0] == ',' ) {
  125. statusStr = "";
  126. format = format[1 .. $];
  127. } else if( format[0] == '{' ) {
  128. format = format[1 .. $];
  129. state = State.Key;
  130. } else {
  131. state = State.Command;
  132. }
  133. if (statusStr.length == 3 && !match) {
  134. auto status = parse!int(statusStr);
  135. match = status == res.statusCode;
  136. }
  137. break;
  138. case State.Key:
  139. auto idx = format.indexOf('}');
  140. enforce(idx > -1, "Missing '}'");
  141. key = format[0 .. idx];
  142. format = format[idx+1 .. $];
  143. state = State.Command;
  144. break;
  145. case State.Command:
  146. if( conditional && negate == match ) {
  147. ln.put('-');
  148. format = format[1 .. $];
  149. state = State.Init;
  150. break;
  151. }
  152. switch(format[0]) {
  153. case 'a': //Remote IP-address
  154. ln.put(req.peer);
  155. break;
  156. //TODO case 'A': //Local IP-address
  157. //case 'B': //Size of Response in bytes, excluding headers
  158. case 'b': //same as 'B' but a '-' is written if no bytes where sent
  159. if (!res.bytesWritten) ln.put('-');
  160. else formattedWrite(() @trusted { return &ln; } (), "%s", res.bytesWritten);
  161. break;
  162. case 'C': //Cookie content {cookie}
  163. import std.algorithm : joiner;
  164. enforce(key != "", "cookie name missing");
  165. auto values = req.cookies.getAll(key);
  166. if (values.length) ln.formattedWrite("%s", values.joiner(";"));
  167. else ln.put("-");
  168. break;
  169. case 'D': //The time taken to serve the request
  170. auto d = res.timeFinalized - req.timeCreated;
  171. formattedWrite(() @trusted { return &ln; } (), "%s", d.total!"msecs"());
  172. break;
  173. //case 'e': //Environment variable {variable}
  174. //case 'f': //Filename
  175. case 'h': //Remote host
  176. ln.put(req.peer);
  177. break;
  178. case 'H': //The request protocol
  179. ln.put("HTTP");
  180. break;
  181. case 'i': //Request header {header}
  182. enforce(key != "", "header name missing");
  183. if (auto pv = key in req.headers) ln.put(*pv);
  184. else ln.put("-");
  185. break;
  186. case 'm': //Request method
  187. ln.put(httpMethodString(req.method));
  188. break;
  189. case 'o': //Response header {header}
  190. enforce(key != "", "header name missing");
  191. if( auto pv = key in res.headers ) ln.put(*pv);
  192. else ln.put("-");
  193. break;
  194. case 'p': //port
  195. formattedWrite(() @trusted { return &ln; } (), "%s", settings.port);
  196. break;
  197. //case 'P': //Process ID
  198. case 'q': //query string (with prepending '?')
  199. ln.put("?");
  200. ln.put(req.queryString);
  201. break;
  202. case 'r': //First line of Request
  203. ln.put(httpMethodString(req.method));
  204. ln.put(' ');
  205. ln.put(req.requestURL);
  206. ln.put(' ');
  207. ln.put(getHTTPVersionString(req.httpVersion));
  208. break;
  209. case 's': //Status
  210. formattedWrite(() @trusted { return &ln; } (), "%s", res.statusCode);
  211. break;
  212. case 't': //Time the request was received {format}
  213. ln.put(req.timeCreated.toSimpleString());
  214. break;
  215. case 'T': //Time taken to server the request in seconds
  216. auto d = res.timeFinalized - req.timeCreated;
  217. formattedWrite(() @trusted { return &ln; } (), "%s", d.total!"seconds");
  218. break;
  219. case 'u': //Remote user
  220. ln.put(req.username.length ? req.username : "-");
  221. break;
  222. case 'U': //The URL path without query string
  223. ln.put(req.requestPath.toString());
  224. break;
  225. case 'v': //Server name
  226. ln.put(req.host.length ? req.host : "-");
  227. break;
  228. default:
  229. throw new Exception("Unknown directive '" ~ format[0] ~ "' in log format string");
  230. }
  231. state = State.Init;
  232. format = format[1 .. $];
  233. break;
  234. }
  235. }
  236. }