1 // Copyright 2011 Google Inc. All Rights Reserved.
3 package com
.google
.appengine
.api
.log
;
5 import com
.google
.appengine
.api
.log
.LogService
.LogLevel
;
6 import com
.google
.common
.collect
.ImmutableList
;
8 import java
.io
.Serializable
;
9 import java
.util
.ArrayList
;
10 import java
.util
.Comparator
;
11 import java
.util
.HashSet
;
12 import java
.util
.List
;
14 import java
.util
.regex
.Matcher
;
15 import java
.util
.regex
.Pattern
;
18 * Allows users to customize the behavior of {@link LogService#fetch(LogQuery)}.
20 * {@code startTime} is the earliest request completion or last-update time
21 * that results should be fetched for, in seconds since the Unix epoch. If
22 * {@code null} then no requests will be excluded for ending too long ago.
24 * {@code endTime} is the latest request completion or last-update time that
25 * results should be fetched for, in seconds since the Unix epoch. If
26 * {@code null} then no requests will be excluded for ending too recently.
28 * {@code offset} is a cursor into the log stream retrieved from a previously
29 * emitted {@link RequestLogs#getOffset}. This iterator will begin returning
30 * logs immediately after the record from which the offset came. If
31 * {@code null}, the query will begin at {@code startTime}.
33 * {@code minLogLevel} is a {@link LogService.LogLevel} which serves as a
34 * filter on the requests returned. Requests with no application logs at or
35 * above the specified level will be omitted. Works even if
36 * {@code includeAppLogs} is not True.
38 * {@code includeIncomplete} selects whether requests that have started but not
39 * yet finished should be included in the query. Defaults to False.
41 * {@code includeAppLogs} specifies whether or not to include application logs
42 * in the query results. Defaults to False.
44 * {@code majorVersionIds} specifies versions of the application's default
45 * module for which logs records should retrieved.
47 * {@code versions} specifies module versions of the application for which
48 * logs should be retrieved.
50 * {@code requestIds}, if not {@code empty()}, indicates that instead of a
51 * time-based scan, logs for the specified requests should be returned.
52 * See the Request IDs section of <a
53 * href="https://developers.google.com/appengine/docs/java/runtime#The_Environment">
54 * the Java Servlet Environment documentation</a> for how to retrieve these IDs
55 * at runtime. Malformed request IDs cause an exception and unrecognized request IDs
56 * are ignored. This option may not be combined with other filtering options such as
57 * startTime, endTime, offset, or minLogLevel. When {@code requestIds} is not {@code empty()},
58 * {@code majorVersionIds} are ignored. Logs are returned in the order requested.
60 * {@code batchSize} specifies the internal batching strategy of the returned
61 * {@link java.lang.Iterable Iterable<RequestLogs>}. Has no impact on the
62 * result of the query.
65 * The recommended way to instantiate a {@code LogQuery} object is to
66 * statically import {@link Builder}.* and invoke a static
67 * creation method followed by an instance mutator (if needed):
70 * import static com.google.appengine.api.log.LogQuery.Builder.*;
74 * // All requests, including application logs.
75 * iter = logService.fetch(withIncludeAppLogs(true));
77 * // All requests ending in the past day (or still running) with an info log or higher.
78 * Calendar cal = Calendar.getInstance();
79 * cal.add(Calendar.DAY_OF_MONTH, -1);
80 * iter = logService.fetch(withEndTimeMillis(cal.time())
81 * .includeIncomplete(true).minimumLogLevel(LogService.INFO));
84 * There are a couple of ways to configure {@link LogQuery} to limit
85 * {@link LogService#fetch(LogQuery)} to only return log records for specific
88 * <li><b>{@link #versions(List)}({@link Builder#withVersions(List)})</b> -
89 * Includes designated module versions for the application.
90 * <li><b>{@link #majorVersionIds(List)} ({@link Builder#withMajorVersionIds(List)})</b> -
91 * Includes designated versions of the default module for the application.
93 * For a particular {@link LogQuery} only one of these methods may be used. If neither is used,
94 * {@link LogService#fetch(LogQuery)} results may include any module version.
96 * It is not allowed to call both {@link #versions(List)} ({@link Builder#withVersions(List)})
97 * and {@link #requestIds(List)}({@link Builder#withRequestIds(List)} for the same {@link LogQuery}.
100 public final class LogQuery
implements Cloneable
, Serializable
{
101 private static final long serialVersionUID
= 3660093076203855168L;
102 static final Comparator
<Version
> VERSION_COMPARATOR
= new VersionComparator();
104 private String offset
;
106 private Long startTimeUsec
;
108 private Long endTimeUsec
;
110 private int batchSize
= LogService
.DEFAULT_ITEMS_PER_FETCH
;
112 private LogLevel minLogLevel
;
113 private boolean includeIncomplete
= false;
114 private boolean includeAppLogs
= false;
115 private List
<String
> majorVersionIds
= new ArrayList
<String
>();
116 private List
<Version
> versions
= new ArrayList
<Version
>();
118 private List
<String
> requestIds
= new ArrayList
<String
>();
120 private static final String MAJOR_VERSION_ID_REGEX
= "[a-z\\d][a-z\\d\\-]{0,99}";
121 private static final Pattern VERSION_PATTERN
= Pattern
.compile(MAJOR_VERSION_ID_REGEX
);
122 private static final String REQUEST_ID_REGEX
= "\\A\\p{XDigit}+\\z";
123 private static final Pattern REQUEST_ID_PATTERN
= Pattern
.compile(REQUEST_ID_REGEX
);
126 * Specifies a version of a module.
128 public static final class Version
implements Serializable
{
129 private static final long serialVersionUID
= 3697597908142270764L;
130 private static final String INVALID_MODULE_ID_MESSAGE_TEMPLATE
= "Invalid module id '%s'";
131 private static final String INVALID_VERSION_ID_MESSAGE_TEMPLATE
= "Invalid version id '%s'";
132 private final String moduleId
;
133 private final String versionId
;
136 * Constructs a {@link Version}.
137 * @param moduleId A valid module id.
138 * @param versionId A valid version id.
140 public Version(String moduleId
, String versionId
) {
141 this.moduleId
= verifyId(moduleId
, INVALID_MODULE_ID_MESSAGE_TEMPLATE
);
142 this.versionId
= verifyId(versionId
, INVALID_VERSION_ID_MESSAGE_TEMPLATE
);
146 * Returns the moduleId.
148 public String
getModuleId() {
153 * Returns the version id.
155 public String
getVersionId() {
160 public int hashCode() {
161 final int prime
= 31;
163 result
= prime
* result
+ ((moduleId
== null) ?
0 : moduleId
.hashCode());
164 result
= prime
* result
+ ((versionId
== null) ?
0 : versionId
.hashCode());
169 public boolean equals(Object obj
) {
176 if (getClass() != obj
.getClass()) {
179 Version other
= (Version
) obj
;
180 if (moduleId
== null) {
181 if (other
.moduleId
!= null) {
184 } else if (!moduleId
.equals(other
.moduleId
)) {
188 if (versionId
== null) {
189 if (other
.versionId
!= null) {
192 } else if (!versionId
.equals(other
.versionId
)) {
199 public String
toString() {
200 return "Version: moduleId=" + moduleId
+ " versionId=" + versionId
;
204 * Verifies and returns a module id or version id.
206 * Note this verification uses the VERSION_PATTERN regular expression which will catch
207 * many but not all syntactic errors.
209 * @param moduleOrVersionId The module or version id to verify.
210 * @param messageTemplate A message template with a placeholder for the invalid value.
211 * @return moduleOrVersionId
212 * @throws IllegalArgumentException if moduleOrVersionId is not valid.
214 private static String
verifyId(String moduleOrVersionId
, String messageTemplate
) {
215 Matcher matcher
= VERSION_PATTERN
.matcher(moduleOrVersionId
);
216 if (!matcher
.matches()) {
217 throw new IllegalArgumentException(String
.format(messageTemplate
, moduleOrVersionId
));
219 return moduleOrVersionId
;
224 * Compares {@link Version} values by {@link Version#getModuleId()} and
225 * breaks ties by comparing by {@link Version#getVersionId()}
227 private static class VersionComparator
implements Comparator
<Version
> {
229 public int compare(Version version1
, Version version2
) {
230 int result
= version1
.getModuleId().compareTo(version2
.getModuleId());
232 result
= version1
.getVersionId().compareTo(version2
.getVersionId());
239 * Contains static creation methods for {@link LogQuery}.
241 public static final class Builder
{
243 * Create a {@link LogQuery} with the given offset.
244 * Shorthand for <code>LogQuery.Builder.withDefaults().offset(offset);</code>.
245 * Please read the {@link LogQuery} class javadoc for an explanation of
246 * how offsets are used.
247 * @param offset the offset to use.
248 * @return The newly created LogQuery instance.
250 public static LogQuery
withOffset(String offset
) {
251 return withDefaults().offset(offset
);
255 * Create a {@link LogQuery} with the given start time.
256 * Shorthand for <code>LogQuery.Builder.withDefaults().startTimeMillis(startTimeMillis);</code>.
257 * Please read the {@link LogQuery} class javadoc for an explanation of
258 * how start time is used.
259 * @param startTimeMillis the start time to use, in milliseconds.
260 * @return The newly created LogQuery instance.
262 public static LogQuery
withStartTimeMillis(long startTimeMillis
) {
263 return withDefaults().startTimeMillis(startTimeMillis
);
267 * Create a {@link LogQuery} with the given start time.
268 * Shorthand for <code>LogQuery.Builder.withDefaults().startTimeUsec(startTimeUsec);</code>.
269 * Please read the {@link LogQuery} class javadoc for an explanation of
270 * how start time is used.
271 * @param startTimeUsec the start time to use, in microseconds.
272 * @return The newly created LogQuery instance.
274 public static LogQuery
withStartTimeUsec(long startTimeUsec
) {
275 return withDefaults().startTimeUsec(startTimeUsec
);
279 * Create a {@link LogQuery} with the given end time.
280 * Shorthand for <code>LogQuery.Builder.withDefaults().endTimeMillis(endTimeMillis);</code>.
281 * Please read the {@link LogQuery} class javadoc for an explanation of
282 * how end time is used.
283 * @param endTimeMillis the start time to use, in milliseconds.
284 * @return The newly created LogQuery instance.
286 public static LogQuery
withEndTimeMillis(long endTimeMillis
) {
287 return withDefaults().endTimeMillis(endTimeMillis
);
291 * Create a {@link LogQuery} with the given end time.
292 * Shorthand for <code>LogQuery.Builder.withDefaults().endTimeUsec(endTimeUsec);</code>.
293 * Please read the {@link LogQuery} class javadoc for an explanation of
294 * how end time is used.
295 * @param endTimeUsec the start time to use, in microseconds.
296 * @return The newly created LogQuery instance.
298 public static LogQuery
withEndTimeUsec(long endTimeUsec
) {
299 return withDefaults().endTimeUsec(endTimeUsec
);
303 * Create a {@link LogQuery} with the given batch size.
304 * Shorthand for <code>LogQuery.Builder.withDefaults().batchSize(batchSize);</code>.
305 * Please read the {@link LogQuery} class javadoc for an explanation of
306 * how batch size is used.
307 * @param batchSize the batch size to set.
308 * @return The newly created LogQuery instance.
310 public static LogQuery
withBatchSize(int batchSize
) {
311 return withDefaults().batchSize(batchSize
);
315 * Create a {@link LogQuery} with the given minimum log level.
316 * Shorthand for <code>LogQuery.Builder.withDefaults().minLogLevel(minLogLevel);</code>.
317 * Please read the {@link LogQuery} class javadoc for an explanation of
318 * how minimum log level is used.
319 * @param minLogLevel the minimum log level to set.
320 * @return The newly created LogQuery instance.
322 public static LogQuery
withMinLogLevel(LogLevel minLogLevel
) {
323 return withDefaults().minLogLevel(minLogLevel
);
327 * Create a {@link LogQuery} with the given include incomplete setting.
329 * <code>LogQuery.Builder.withDefaults().includeIncomplete(includeIncomplete);</code>.
330 * Please read the {@link LogQuery} class javadoc for an explanation of
331 * how include incomplete is used.
332 * @param includeIncomplete the inclusion value to set.
333 * @return The newly created LogQuery instance.
335 public static LogQuery
withIncludeIncomplete(boolean includeIncomplete
) {
336 return withDefaults().includeIncomplete(includeIncomplete
);
340 * Create a {@link LogQuery} with include application logs set.
341 * Shorthand for <code>LogQuery.Builder.withDefaults().includeAppLogs(includeAppLogs);</code>.
342 * Please read the {@link LogQuery} class javadoc for an explanation of
343 * the include application logs setting.
344 * @param includeAppLogs the inclusion value to set.
345 * @return The newly created LogQuery instance.
347 public static LogQuery
withIncludeAppLogs(boolean includeAppLogs
) {
348 return withDefaults().includeAppLogs(includeAppLogs
);
352 * Create a {@link LogQuery} with the given major version IDs.
353 * Shorthand for <code>LogQuery.Builder.withDefaults().majorVersionIds(versionIds);</code>.
354 * Please read the {@link LogQuery} class javadoc for an explanation of
355 * how the list of major version ids is used.
356 * @param versionIds the major version id list to set.
357 * @return The newly created LogQuery instance.
359 public static LogQuery
withMajorVersionIds(List
<String
> versionIds
) {
360 return withDefaults().majorVersionIds(versionIds
);
364 * Create a {@link LogQuery} with the given {@link Version} values.
366 * <code>LogQuery.Builder.withDefaults().versions(versions);</code>.
367 * Please read the {@link LogQuery} class javadoc for usage information.
368 * @param versions the list to set.
369 * @return The newly created LogQuery instance.
371 public static LogQuery
withVersions(List
<Version
> versions
) {
372 return withDefaults().versions(versions
);
376 * Create a {@link LogQuery} with the given request IDs.
377 * Shorthand for <code>LogQuery.Builder.withDefaults().requestIds(requestIds);</code>.
378 * See the {@link LogQuery} class javadoc for an explanation of
379 * how the list of request ids is used.
380 * @param requestIds the request id list to set.
381 * @return The newly created LogQuery instance.
382 * @since App Engine 1.7.4.
384 public static LogQuery
withRequestIds(List
<String
> requestIds
) {
385 return withDefaults().requestIds(requestIds
);
389 * Helper method for creating a {@link LogQuery} instance with
390 * default values. Please read the {@link LogQuery} class javadoc for an
391 * explanation of the defaults.
393 public static LogQuery
withDefaults() {
394 return new LogQuery();
399 * Makes a copy of a provided LogQuery.
401 * @return A new LogQuery whose fields are copied from the given LogQuery.
404 public LogQuery
clone() {
407 clone
= (LogQuery
) super.clone();
408 } catch (CloneNotSupportedException e
) {
409 throw new RuntimeException(e
);
412 clone
.majorVersionIds
= new ArrayList
<String
>(majorVersionIds
);
413 clone
.requestIds
= new ArrayList
<String
>(requestIds
);
414 clone
.versions
= new ArrayList
<Version
>(versions
);
419 * Sets the offset. Please read the class javadoc for an explanation of
420 * how offset is used.
421 * @param offset The offset to set.
422 * @return {@code this} (for chaining)
424 public LogQuery
offset(String offset
) {
425 this.offset
= offset
;
430 * Sets the start time to a value in milliseconds. Please read the class
431 * javadoc for an explanation of how start time is used.
432 * @param startTimeMillis The start time to set, in milliseconds.
433 * @return {@code this} (for chaining)
435 public LogQuery
startTimeMillis(long startTimeMillis
) {
436 this.startTimeUsec
= startTimeMillis
* 1000;
441 * Sets the start time to a value in microseconds. Please read the class
442 * javadoc for an explanation of how start time is used.
443 * @param startTimeUsec The start time to set, in microseconds.
444 * @return {@code this} (for chaining)
446 public LogQuery
startTimeUsec(long startTimeUsec
) {
447 this.startTimeUsec
= startTimeUsec
;
452 * Sets the end time to a value in milliseconds. Please read the class
453 * javadoc for an explanation of how end time is used.
454 * @param endTimeMillis The end time to set, in milliseconds.
455 * @return {@code this} (for chaining)
457 public LogQuery
endTimeMillis(long endTimeMillis
) {
458 this.endTimeUsec
= endTimeMillis
* 1000;
463 * Sets the end time to a value in microseconds. Please read the class
464 * javadoc for an explanation of how end time is used.
465 * @param endTimeUsec The end time to set, in microseconds.
466 * @return {@code this} (for chaining)
468 public LogQuery
endTimeUsec(long endTimeUsec
) {
469 this.endTimeUsec
= endTimeUsec
;
474 * Sets the batch size. Please read the class javadoc for an explanation of
475 * how batch size is used.
476 * @param batchSize The batch size to set. Must be greater than 0.
477 * @return {@code this} (for chaining)
479 public LogQuery
batchSize(int batchSize
) {
481 throw new IllegalArgumentException("batchSize must be greater than zero");
484 this.batchSize
= batchSize
;
489 * Sets the minimum log level. Please read the class javadoc for an
490 * explanation of how minimum log level is used.
491 * @param minLogLevel The minimum log level to set.
492 * @return {@code this} (for chaining)
494 public LogQuery
minLogLevel(LogLevel minLogLevel
) {
495 this.minLogLevel
= minLogLevel
;
500 * Sets include incomplete. Please read the class javadoc for an
501 * explanation of how include incomplete is used.
502 * @param includeIncomplete The value to set.
503 * @return {@code this} (for chaining)
505 public LogQuery
includeIncomplete(boolean includeIncomplete
) {
506 this.includeIncomplete
= includeIncomplete
;
511 * Sets include application logs. Please read the class javadoc for an
512 * explanation of how include application logs is used.
513 * @param includeAppLogs The value to set.
514 * @return {@code this} (for chaining)
516 public LogQuery
includeAppLogs(boolean includeAppLogs
) {
517 this.includeAppLogs
= includeAppLogs
;
522 * Sets the major version identifiers to query. Please read the class
523 * javadoc for an explanation of how major versions are used.
524 * @param versionIds The major version identifier list to set.
525 * @return {@code this} (for chaining)
527 public LogQuery
majorVersionIds(List
<String
> versionIds
) {
528 if (!versions
.isEmpty()) {
529 throw new IllegalStateException(
530 "LogQuery.majorVersionIds may not be called after LogQuery.versions.");
533 for (String versionId
: versionIds
) {
534 Matcher matcher
= VERSION_PATTERN
.matcher(versionId
);
535 if (!matcher
.matches()) {
536 throw new IllegalArgumentException("versionIds must only contain valid " +
537 "major version identifiers. Version " + versionId
+ " is not a valid " +
538 "major version identifier.");
542 this.majorVersionIds
= versionIds
;
547 * Restricts the query to log records for the specified module versions.
549 * Please read the class javadoc for usage information.
551 * @param versions The list of module versions to query.
552 * @return {@code this} (for chaining)
554 public LogQuery
versions(List
<Version
> versions
) {
555 if (!this.majorVersionIds
.isEmpty()) {
556 throw new IllegalStateException(
557 "LogQuery.versions may not be called after LogQuery.majorVersionIds.");
560 if (!requestIds
.isEmpty()) {
561 throw new IllegalStateException(
562 "LogQuery.versions may not be called after LogQuery.requestIds.");
565 this.versions
.clear();
566 this.versions
.addAll(versions
);
571 * Sets the list of request ids to query. See the class javadoc for an
572 * explanation of how request ids are used.
573 * @param requestIds The request id list to set.
574 * @return {@code this} (for chaining)
576 public LogQuery
requestIds(List
<String
> requestIds
) {
577 if (!versions
.isEmpty()) {
578 throw new IllegalStateException(
579 "LogQuery.requestIds may not be called after LogQuery.versions.");
582 Set
<String
> seen
= new HashSet
<String
>();
583 for (String requestId
: requestIds
) {
584 if (!seen
.add(requestId
)) {
585 throw new IllegalArgumentException("requestIds must be unique.");
588 Matcher matcher
= REQUEST_ID_PATTERN
.matcher(requestId
);
589 if (!matcher
.matches()) {
590 throw new IllegalArgumentException("requestIds must only contain valid " +
591 "request ids. " + requestId
+ " is not a valid " +
596 this.requestIds
= requestIds
;
601 * @return The offset, or {@code null} if none was provided.
603 public String
getOffset() {
608 * @return The batch size, or {@code null} if none was provided.
610 public Integer
getBatchSize() {
615 * @return The end time in milliseconds, or {@code null} if none was provided.
617 public Long
getEndTimeMillis() {
618 return endTimeUsec
!= null ? endTimeUsec
/ 1000 : null;
622 * @return The end time in microseconds, or {@code null} if none was provided.
624 public Long
getEndTimeUsec() {
629 * @return Whether or not application logs should be returned.
631 public Boolean
getIncludeAppLogs() {
632 return includeAppLogs
;
636 * @return Whether or not incomplete request logs should be returned.
638 public Boolean
getIncludeIncomplete() {
639 return includeIncomplete
;
643 * @return The minimum log level, or {@code null} if none was provided.
645 public LogLevel
getMinLogLevel() {
650 * @return The start time in milliseconds, or {@code null} if none was provided.
652 public Long
getStartTimeMillis() {
653 return startTimeUsec
!= null ? startTimeUsec
/ 1000 : null;
657 * @return The start time in microseconds, or {@code null} if none was provided.
659 public Long
getStartTimeUsec() {
660 return startTimeUsec
;
664 * @return The list of major app versions that should be queried over, or
665 * an empty list if none were set.
667 public List
<String
> getMajorVersionIds() {
668 return majorVersionIds
;
672 * @return The list possibly empty list of module versions that should be queried over.
674 public List
<Version
> getVersions() {
675 ImmutableList
.Builder
<Version
> builder
= ImmutableList
.builder();
676 return builder
.addAll(versions
).build();
680 * @return The list of request ids that should be queried over, or
681 * {@code null} if none were set.
683 public List
<String
> getRequestIds() {