2121import com .google .apphosting .base .protos .AppLogsPb .AppLogGroup ;
2222import com .google .apphosting .base .protos .AppLogsPb .AppLogLine ;
2323import com .google .apphosting .base .protos .RuntimePb .UPResponse ;
24- import com .google .apphosting .base .protos .SourcePb .SourceLocation ;
2524import com .google .common .annotations .VisibleForTesting ;
2625import com .google .common .base .Stopwatch ;
27- import com .google .common .base .Throwables ;
2826import com .google .common .collect .ImmutableList ;
2927import com .google .common .flogger .GoogleLogger ;
28+ import java .time .Duration ;
3029import java .util .ArrayList ;
3130import java .util .List ;
3231import java .util .concurrent .ExecutionException ;
3332import java .util .concurrent .Future ;
3433import java .util .logging .Level ;
35- import java .util .regex .Pattern ;
3634import javax .annotation .concurrent .GuardedBy ;
3735
3836/**
@@ -77,17 +75,6 @@ public class AppLogsWriter {
7775 static final String LOG_TRUNCATED_SUFFIX = "\n <truncated>" ;
7876 static final int LOG_TRUNCATED_SUFFIX_LENGTH = LOG_TRUNCATED_SUFFIX .length ();
7977
80- // This regular expression should match a leading prefix of all
81- // sensitive class names that are to be disregarded for the purposes
82- // of finding the log source location.
83- private static final String PROTECTED_LOGS_CLASSES_REGEXP =
84- "(com\\ .google\\ .apphosting\\ .runtime\\ .security"
85- + "|java\\ .lang\\ .reflect"
86- + "|java\\ .lang\\ .invoke"
87- + "|java\\ .security"
88- + "|sun\\ .reflect"
89- + ")\\ ..+" ;
90-
9178 private final Object lock = new Object ();
9279
9380 private final int maxLogMessageLength ;
@@ -105,8 +92,6 @@ public class AppLogsWriter {
10592 private Future <byte []> currentFlush ;
10693 @ GuardedBy ("lock" )
10794 private Stopwatch stopwatch ;
108- private static final Pattern PROTECTED_LOGS_CLASSES =
109- Pattern .compile (PROTECTED_LOGS_CLASSES_REGEXP );
11095
11196 public AppLogsWriter (
11297 MutableUpResponse upResponse ,
@@ -192,14 +177,6 @@ public void addLogRecordAndMaybeFlush(ApiProxy.LogRecord fullRecord) {
192177 .setTimestampUsec (record .getTimestamp ())
193178 .setMessage (record .getMessage ());
194179
195- StackTraceElement frame = stackFrameFor (record .getStackFrame (), record .getSourceLocation ());
196- if (frame != null ) {
197- SourceLocation sourceLocation = getSourceLocationProto (frame );
198- if (sourceLocation != null ) {
199- logLineBuilder .setSourceLocation (sourceLocation );
200- }
201- }
202-
203180 appLogLines .add (logLineBuilder .build ());
204181 }
205182
@@ -228,7 +205,8 @@ private void addLogLinesAndMaybeFlush(Iterable<AppLogLine> appLogLines) {
228205 currentByteCount += serializedSize ;
229206 }
230207
231- if (maxSecondsBetweenFlush > 0 && stopwatch .elapsed ().getSeconds () >= maxSecondsBetweenFlush ) {
208+ if (maxSecondsBetweenFlush > 0
209+ && stopwatch .elapsed ().compareTo (Duration .ofSeconds (maxSecondsBetweenFlush )) >= 0 ) {
232210 waitForCurrentFlushAndStartNewFlush ();
233211 }
234212 }
@@ -398,69 +376,6 @@ long getByteCountBeforeFlushing() {
398376 return maxBytesToFlush ;
399377 }
400378
401- /**
402- * Converts the stack trace stored in the Throwable into a SourceLocation
403- * proto. Heuristics are applied to strip out non user code, such as the App
404- * Engine logging infrastructure, and the servlet engine. Heuristics are also
405- * employed to convert class paths into file names.
406- */
407- @ VisibleForTesting
408- SourceLocation getSourceLocationProto (StackTraceElement sourceLocationFrame ) {
409- if (sourceLocationFrame == null || sourceLocationFrame .getFileName () == null ) {
410- return null ;
411- }
412- return SourceLocation .newBuilder ()
413- .setFile (sourceLocationFrame .getFileName ())
414- .setLine (sourceLocationFrame .getLineNumber ())
415- .setFunctionName (
416- sourceLocationFrame .getClassName () + "." + sourceLocationFrame .getMethodName ())
417- .build ();
418- }
419-
420- /**
421- * Rewrites the given StackTraceElement with a filename and line number found by looking through
422- * the given Throwable for a frame that has the same class and method as the input
423- * StackTraceElement. If the input frame already has source information then just return it
424- * unchanged.
425- */
426- private static StackTraceElement stackFrameFor (StackTraceElement frame , Throwable stack ) {
427- if (frame == null ) {
428- // No user-provided stack frame.
429- if (stack == null ) {
430- return null ;
431- }
432- return getTopUserStackFrame (Throwables .lazyStackTrace (stack ));
433- }
434-
435- // If we have a user-provided file:line, use it.
436- if (frame .getFileName () != null && frame .getLineNumber () > 0 ) {
437- return frame ;
438- }
439-
440- // We should have a Throwable given the preceding, but if for some reason we don't, avoid
441- // throwing NullPointerException.
442- if (stack == null ) {
443- return null ;
444- }
445-
446- return findStackFrame (frame .getClassName (), frame .getMethodName (), stack );
447- }
448-
449- /** Searches for the stack frame where the Throwable matches the provided class and method. */
450- static StackTraceElement findStackFrame (String className , String methodName , Throwable stack ) {
451- List <StackTraceElement > stackFrames = Throwables .lazyStackTrace (stack );
452- for (StackTraceElement stackFrame : stackFrames ) {
453- if (className .equals (stackFrame .getClassName ())
454- && methodName .equals (stackFrame .getMethodName ())) {
455- return stackFrame ;
456- }
457- }
458-
459- // No matching stack frame was found, return the top user frame, which should be
460- // the one that called the log method.
461- return AppLogsWriter .getTopUserStackFrame (stackFrames );
462- }
463-
464379 /**
465380 * Converts from a Java Logging level to an App Engine logging level.
466381 * SEVERE maps to error, WARNING to warn, INFO to info, and all
@@ -484,33 +399,4 @@ public static ApiProxy.LogRecord.Level convertLogLevel(Level level) {
484399 }
485400 }
486401
487- /**
488- * Analyzes a stack trace and returns the topmost frame that contains user
489- * code, so as to filter out App Engine logging and servlet infrastructure
490- * and just return frames relevant to user code.
491- */
492- public static StackTraceElement getTopUserStackFrame (List <StackTraceElement > stack ) {
493- // Find the top-most stack frame in code that belongs to the user.
494- boolean loggerFrameEncountered = false ; // Set on the first java.util.logging.Logger frame
495- for (StackTraceElement element : stack ) {
496- if (isLoggerFrame (element .getClassName ())) {
497- loggerFrameEncountered = true ;
498- } else if (loggerFrameEncountered ) {
499- // Skip protected frames, e.g., mirrors.
500- if (!isProtectedFrame (element .getClassName ())) {
501- return element ;
502- }
503- }
504- }
505- return null ;
506- }
507-
508- private static boolean isLoggerFrame (String cname ) {
509- return cname .equals ("java.util.logging.Logger" )
510- || cname .equals ("com.google.devtools.cdbg.debuglets.java.GaeDynamicLogHelper" );
511- }
512-
513- private static boolean isProtectedFrame (String cname ) {
514- return PROTECTED_LOGS_CLASSES .matcher (cname ).lookingAt ();
515- }
516402}
0 commit comments