001/** 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v1.0 as published by 007 * the Eclipse Foundation 008 * 009 * or (per the licensee's choosing) 010 * 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014package ch.qos.logback.access.spi; 015 016import ch.qos.logback.access.AccessConstants; 017import ch.qos.logback.access.pattern.AccessConverter; 018import ch.qos.logback.access.servlet.Util; 019import ch.qos.logback.core.Context; 020import ch.qos.logback.core.spi.SequenceNumberGenerator; 021 022import javax.servlet.http.Cookie; 023import javax.servlet.http.HttpServletRequest; 024import javax.servlet.http.HttpServletResponse; 025import javax.servlet.http.HttpSession; 026 027import java.io.Serializable; 028import java.util.ArrayList; 029import java.util.Enumeration; 030import java.util.HashMap; 031import java.util.List; 032import java.util.Map; 033import java.util.TreeMap; 034import java.util.Vector; 035 036// Contributors: Joern Huxhorn (see also bug #110) 037 038/** 039 * The Access module's internal representation of logging events. When the 040 * logging component instance is called in the container to log then a 041 * <code>AccessEvent</code> instance is created. This instance is passed around 042 * to the different logback components. 043 * 044 * @author Ceki Gülcü 045 * @author Sébastien Pennec 046 */ 047public class AccessEvent implements Serializable, IAccessEvent { 048 049 private static final String[] NA_STRING_ARRAY = new String[] { NA }; 050 051 private static final long serialVersionUID = 866718993618836343L; 052 053 private static final String EMPTY = ""; 054 055 private transient final HttpServletRequest httpRequest; 056 private transient final HttpServletResponse httpResponse; 057 058 String queryString; 059 String requestURI; 060 String requestURL; 061 String remoteHost; 062 String remoteUser; 063 String remoteAddr; 064 String threadName; 065 String protocol; 066 String method; 067 String serverName; 068 String requestContent; 069 String responseContent; 070 String sessionID; 071 long elapsedTime; 072 073 Map<String, String> requestHeaderMap; 074 Map<String, String[]> requestParameterMap; 075 Map<String, String> responseHeaderMap; 076 Map<String, Object> attributeMap; 077 078 long contentLength = SENTINEL; 079 int statusCode = SENTINEL; 080 int localPort = SENTINEL; 081 082 transient ServerAdapter serverAdapter; 083 084 /** 085 * The number of milliseconds elapsed from 1/1/1970 until logging event was 086 * created. 087 */ 088 private long timeStamp = 0; 089 090 private long sequenceNumber = 0; 091 092 public AccessEvent(Context context, HttpServletRequest httpRequest, HttpServletResponse httpResponse, 093 ServerAdapter adapter) { 094 this.httpRequest = httpRequest; 095 this.httpResponse = httpResponse; 096 this.timeStamp = System.currentTimeMillis(); 097 098 SequenceNumberGenerator sng = context.getSequenceNumberGenerator(); 099 if (sng != null) { 100 this.sequenceNumber = sng.nextSequenceNumber(); 101 } 102 this.serverAdapter = adapter; 103 this.elapsedTime = calculateElapsedTime(); 104 } 105 106 /** 107 * Returns the underlying HttpServletRequest. After serialization the returned 108 * value will be null. 109 * 110 * @return 111 */ 112 @Override 113 public HttpServletRequest getRequest() { 114 return httpRequest; 115 } 116 117 /** 118 * Returns the underlying HttpServletResponse. After serialization the returned 119 * value will be null. 120 * 121 * @return 122 */ 123 @Override 124 public HttpServletResponse getResponse() { 125 return httpResponse; 126 } 127 128 @Override 129 public long getTimeStamp() { 130 return timeStamp; 131 } 132 133 public void setTimeStamp(long timeStamp) { 134 if (this.timeStamp != 0) { 135 throw new IllegalStateException("timeStamp has been already set for this event."); 136 } else { 137 this.timeStamp = timeStamp; 138 } 139 } 140 141 public long getSequenceNumber() { 142 return sequenceNumber; 143 } 144 145 public void setSequenceNumber(long sequenceNumber) { 146 this.sequenceNumber = sequenceNumber; 147 } 148 149 /** 150 * @param threadName The threadName to set. 151 */ 152 public void setThreadName(String threadName) { 153 this.threadName = threadName; 154 } 155 156 @Override 157 public String getThreadName() { 158 return threadName == null ? NA : threadName; 159 } 160 161 @Override 162 public String getRequestURI() { 163 if (requestURI == null) { 164 if (httpRequest != null) { 165 requestURI = httpRequest.getRequestURI(); 166 } else { 167 requestURI = NA; 168 } 169 } 170 return requestURI; 171 } 172 173 @Override 174 public String getQueryString() { 175 if (queryString == null) { 176 if (httpRequest != null) { 177 StringBuilder buf = new StringBuilder(); 178 final String qStr = httpRequest.getQueryString(); 179 if (qStr != null) { 180 buf.append(AccessConverter.QUESTION_CHAR); 181 buf.append(qStr); 182 } 183 queryString = buf.toString(); 184 } else { 185 queryString = NA; 186 } 187 } 188 return queryString; 189 } 190 191 /** 192 * The first line of the request. 193 */ 194 @Override 195 public String getRequestURL() { 196 if (requestURL == null) { 197 if (httpRequest != null) { 198 StringBuilder buf = new StringBuilder(); 199 buf.append(httpRequest.getMethod()); 200 buf.append(AccessConverter.SPACE_CHAR); 201 buf.append(httpRequest.getRequestURI()); 202 buf.append(getQueryString()); 203 buf.append(AccessConverter.SPACE_CHAR); 204 buf.append(httpRequest.getProtocol()); 205 requestURL = buf.toString(); 206 } else { 207 requestURL = NA; 208 } 209 } 210 return requestURL; 211 } 212 213 @Override 214 public String getRemoteHost() { 215 if (remoteHost == null) { 216 if (httpRequest != null) { 217 // the underlying implementation of HttpServletRequest will 218 // determine if remote lookup will be performed 219 remoteHost = httpRequest.getRemoteHost(); 220 } else { 221 remoteHost = NA; 222 } 223 } 224 return remoteHost; 225 } 226 227 @Override 228 public String getRemoteUser() { 229 if (remoteUser == null) { 230 if (httpRequest != null) { 231 remoteUser = httpRequest.getRemoteUser(); 232 } else { 233 remoteUser = NA; 234 } 235 } 236 return remoteUser; 237 } 238 239 @Override 240 public String getProtocol() { 241 if (protocol == null) { 242 if (httpRequest != null) { 243 protocol = httpRequest.getProtocol(); 244 } else { 245 protocol = NA; 246 } 247 } 248 return protocol; 249 } 250 251 @Override 252 public String getMethod() { 253 if (method == null) { 254 if (httpRequest != null) { 255 method = httpRequest.getMethod(); 256 } else { 257 method = NA; 258 } 259 } 260 return method; 261 } 262 263 @Override 264 public String getSessionID() { 265 if (sessionID == null) { 266 if (httpRequest != null) { 267 final HttpSession session = httpRequest.getSession(false); 268 if (session != null) { 269 sessionID = session.getId(); 270 } 271 } else { 272 sessionID = NA; 273 } 274 } 275 return sessionID; 276 } 277 278 @Override 279 public String getServerName() { 280 if (serverName == null) { 281 if (httpRequest != null) { 282 serverName = httpRequest.getServerName(); 283 } else { 284 serverName = NA; 285 } 286 } 287 return serverName; 288 } 289 290 @Override 291 public String getRemoteAddr() { 292 if (remoteAddr == null) { 293 if (httpRequest != null) { 294 remoteAddr = httpRequest.getRemoteAddr(); 295 } else { 296 remoteAddr = NA; 297 } 298 } 299 return remoteAddr; 300 } 301 302 @Override 303 public String getRequestHeader(String key) { 304 String result = null; 305 key = key.toLowerCase(); 306 if (requestHeaderMap == null) { 307 if (httpRequest != null) { 308 buildRequestHeaderMap(); 309 result = requestHeaderMap.get(key); 310 } 311 } else { 312 result = requestHeaderMap.get(key); 313 } 314 315 if (result != null) { 316 return result; 317 } else { 318 return NA; 319 } 320 } 321 322 @Override 323 public Enumeration<String> getRequestHeaderNames() { 324 // post-serialization 325 if (httpRequest == null) { 326 Vector<String> list = new Vector<String>(getRequestHeaderMap().keySet()); 327 return list.elements(); 328 } 329 return httpRequest.getHeaderNames(); 330 } 331 332 @Override 333 public Map<String, String> getRequestHeaderMap() { 334 if (requestHeaderMap == null) { 335 buildRequestHeaderMap(); 336 } 337 return requestHeaderMap; 338 } 339 340 public void buildRequestHeaderMap() { 341 // according to RFC 2616 header names are case-insensitive 342 // latest versions of Tomcat return header names in lower-case 343 requestHeaderMap = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER); 344 Enumeration<String> e = httpRequest.getHeaderNames(); 345 if (e == null) { 346 return; 347 } 348 while (e.hasMoreElements()) { 349 String key = e.nextElement(); 350 requestHeaderMap.put(key, httpRequest.getHeader(key)); 351 } 352 } 353 354 public void buildRequestParameterMap() { 355 requestParameterMap = new HashMap<String, String[]>(); 356 try { 357 Enumeration<String> e = httpRequest.getParameterNames(); 358 if (e == null) { 359 return; 360 } 361 while (e.hasMoreElements()) { 362 String key = e.nextElement(); 363 requestParameterMap.put(key, httpRequest.getParameterValues(key)); 364 } 365 } catch(Throwable t) { 366 // The use of HttpServletRequest.getParameterNames() can cause 367 // a READ of the Request body content. This can fail with various 368 // Throwable failures depending on the state of the Request 369 // at the time this method is called. 370 // We don't want to fail the logging due to these types of requests 371 t.printStackTrace(); 372 } 373 } 374 375 @Override 376 public Map<String, String[]> getRequestParameterMap() { 377 if (requestParameterMap == null) { 378 buildRequestParameterMap(); 379 } 380 return requestParameterMap; 381 } 382 383 @Override 384 public String getAttribute(String key) { 385 Object value = null; 386 if (attributeMap != null) { 387 // Event was prepared for deferred processing so we have a copy of attribute map 388 // and must use that copy 389 value = attributeMap.get(key); 390 } else if (httpRequest != null) { 391 // We have original request so take attribute from it 392 value = httpRequest.getAttribute(key); 393 } 394 395 return value != null ? value.toString() : NA; 396 } 397 398 private void copyAttributeMap() { 399 400 if (httpRequest == null) { 401 return; 402 } 403 404 // attributeMap has been copied already. See also LOGBACK-1189 405 if (attributeMap != null) { 406 return; 407 } 408 409 attributeMap = new HashMap<String, Object>(); 410 411 Enumeration<String> names = httpRequest.getAttributeNames(); 412 while (names.hasMoreElements()) { 413 String name = names.nextElement(); 414 415 Object value = httpRequest.getAttribute(name); 416 if (shouldCopyAttribute(name, value)) { 417 attributeMap.put(name, value); 418 } 419 } 420 } 421 422 private boolean shouldCopyAttribute(String name, Object value) { 423 if (AccessConstants.LB_INPUT_BUFFER.equals(name) || AccessConstants.LB_OUTPUT_BUFFER.equals(name)) { 424 // Do not copy attributes used by logback internally - these are available via 425 // other getters anyway 426 return false; 427 } else if (value == null) { 428 // No reasons to copy nulls - Map.get() will return null for missing keys and 429 // the list of attribute 430 // names is not available through IAccessEvent 431 return false; 432 } else { 433 // Only copy what is serializable 434 return value instanceof Serializable; 435 } 436 } 437 438 @Override 439 public String[] getRequestParameter(String key) { 440 String[] value = null; 441 442 if (requestParameterMap != null) { 443 value = requestParameterMap.get(key); 444 } else if (httpRequest != null) { 445 value = httpRequest.getParameterValues(key); 446 } 447 448 return (value != null) ? value : NA_STRING_ARRAY; 449 } 450 451 @Override 452 public String getCookie(String key) { 453 454 if (httpRequest != null) { 455 Cookie[] cookieArray = httpRequest.getCookies(); 456 if (cookieArray == null) { 457 return NA; 458 } 459 460 for (Cookie cookie : cookieArray) { 461 if (key.equals(cookie.getName())) { 462 return cookie.getValue(); 463 } 464 } 465 } 466 return NA; 467 } 468 469 @Override 470 public long getContentLength() { 471 if (contentLength == SENTINEL) { 472 if (httpResponse != null) { 473 contentLength = serverAdapter.getContentLength(); 474 return contentLength; 475 } 476 } 477 return contentLength; 478 } 479 480 public int getStatusCode() { 481 if (statusCode == SENTINEL) { 482 if (httpResponse != null) { 483 statusCode = serverAdapter.getStatusCode(); 484 } 485 } 486 return statusCode; 487 } 488 489 public long getElapsedSeconds() { 490 return elapsedTime < 0 ? elapsedTime : elapsedTime / 1000; 491 } 492 493 public long getElapsedTime() { 494 return elapsedTime; 495 } 496 497 private long calculateElapsedTime() { 498 if (serverAdapter.getRequestTimestamp() < 0) { 499 return -1; 500 } 501 return getTimeStamp() - serverAdapter.getRequestTimestamp(); 502 } 503 504 public String getRequestContent() { 505 if (requestContent != null) { 506 return requestContent; 507 } 508 509 if (Util.isFormUrlEncoded(httpRequest)) { 510 StringBuilder buf = new StringBuilder(); 511 512 try { 513 Enumeration<String> pramEnumeration = httpRequest.getParameterNames(); 514 515 // example: id=1234&user=cgu 516 // number=1233&x=1 517 int count = 0; 518 while (pramEnumeration.hasMoreElements()) { 519 520 String key = pramEnumeration.nextElement(); 521 if (count++ != 0) { 522 buf.append("&"); 523 } 524 buf.append(key); 525 buf.append("="); 526 String val = httpRequest.getParameter(key); 527 if (val != null) { 528 buf.append(val); 529 } else { 530 buf.append(""); 531 } 532 } 533 } catch (Throwable t) { 534 // The use of HttpServletRequest.getParameterNames() and 535 // HttpServletRequest.getParameter(String) can cause 536 // a READ of the Request body content. This can fail with various 537 // Throwable failures depending on the state of the Request 538 // at the time this method is called. 539 // We don't want to fail the logging due to these types of requests 540 t.printStackTrace(); 541 } 542 requestContent = buf.toString(); 543 } else { 544 // retrieve the byte array placed by TeeFilter 545 byte[] inputBuffer = (byte[]) httpRequest.getAttribute(AccessConstants.LB_INPUT_BUFFER); 546 547 if (inputBuffer != null) { 548 requestContent = new String(inputBuffer); 549 } 550 551 if (requestContent == null || requestContent.length() == 0) { 552 requestContent = EMPTY; 553 } 554 } 555 556 return requestContent; 557 } 558 559 public String getResponseContent() { 560 if (responseContent != null) { 561 return responseContent; 562 } 563 564 if (Util.isImageResponse(httpResponse)) { 565 responseContent = "[IMAGE CONTENTS SUPPRESSED]"; 566 } else { 567 568 // retrieve the byte array previously placed by TeeFilter 569 byte[] outputBuffer = (byte[]) httpRequest.getAttribute(AccessConstants.LB_OUTPUT_BUFFER); 570 571 if (outputBuffer != null) { 572 responseContent = new String(outputBuffer); 573 } 574 if (responseContent == null || responseContent.length() == 0) { 575 responseContent = EMPTY; 576 } 577 } 578 579 return responseContent; 580 } 581 582 public int getLocalPort() { 583 if (localPort == SENTINEL) { 584 if (httpRequest != null) { 585 localPort = httpRequest.getLocalPort(); 586 } 587 588 } 589 return localPort; 590 } 591 592 public ServerAdapter getServerAdapter() { 593 return serverAdapter; 594 } 595 596 public String getResponseHeader(String key) { 597 buildResponseHeaderMap(); 598 return responseHeaderMap.get(key); 599 } 600 601 void buildResponseHeaderMap() { 602 if (responseHeaderMap == null) { 603 responseHeaderMap = serverAdapter.buildResponseHeaderMap(); 604 } 605 } 606 607 public Map<String, String> getResponseHeaderMap() { 608 buildResponseHeaderMap(); 609 return responseHeaderMap; 610 } 611 612 public List<String> getResponseHeaderNameList() { 613 buildResponseHeaderMap(); 614 return new ArrayList<String>(responseHeaderMap.keySet()); 615 } 616 617 public void prepareForDeferredProcessing() { 618 getRequestHeaderMap(); 619 getRequestParameterMap(); 620 getResponseHeaderMap(); 621 getLocalPort(); 622 getMethod(); 623 getProtocol(); 624 getRemoteAddr(); 625 getRemoteHost(); 626 getRemoteUser(); 627 getRequestURI(); 628 getRequestURL(); 629 getServerName(); 630 getTimeStamp(); 631 getElapsedTime(); 632 633 getStatusCode(); 634 getContentLength(); 635 getRequestContent(); 636 getResponseContent(); 637 638 copyAttributeMap(); 639 } 640}