• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.providers.media.util;
18 
19 import static java.nio.file.StandardOpenOption.APPEND;
20 import static java.nio.file.StandardOpenOption.CREATE;
21 
22 import android.util.Log;
23 
24 import androidx.annotation.GuardedBy;
25 import androidx.annotation.NonNull;
26 import androidx.annotation.Nullable;
27 
28 import java.io.File;
29 import java.io.IOException;
30 import java.io.PrintWriter;
31 import java.io.Writer;
32 import java.nio.file.Files;
33 import java.nio.file.Path;
34 import java.text.SimpleDateFormat;
35 import java.util.Date;
36 import java.util.Locale;
37 import java.util.stream.Stream;
38 
39 public class LegacyLogging {
40     public static final String TAG = "LegacyMediaProvider";
41 
42     /** Size limit of each persistent log file, in bytes */
43     private static final int PERSISTENT_SIZE = 32 * 1024;
44     private static final SimpleDateFormat DATE_FORMAT =
45             new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.ROOT);
46     private static final Object LOCK = new Object();
47 
48     @GuardedBy("LOCK")
49     private static Path sPersistentDir;
50     @GuardedBy("LOCK")
51     private static Path sPersistentFile;
52     @GuardedBy("LOCK")
53     private static Writer sWriter;
54 
55     /**
56      * Initialize persistent logging which is then available through
57      * {@link #logPersistent(String)} and {@link #dumpPersistent(PrintWriter)}.
58      */
initPersistent(@onNull File persistentDir)59     public static void initPersistent(@NonNull File persistentDir) {
60         synchronized (LOCK) {
61             sPersistentDir = persistentDir.toPath();
62             closeWriterAndUpdatePathLocked(null);
63         }
64     }
65 
66     /**
67      * Write the given message to persistent logs.
68      */
logPersistent(@onNull String format, @Nullable Object ... args)69     public static void logPersistent(@NonNull String format, @Nullable Object ... args) {
70         final String msg = (args == null || args.length == 0)
71                 ? format : String.format(Locale.ROOT, format, args);
72 
73         Log.i(TAG, msg);
74 
75         synchronized (LOCK) {
76             if (sPersistentDir == null) return;
77 
78             try {
79                 Path path = resolveCurrentPersistentFileLocked();
80                 if (!path.equals(sPersistentFile)) {
81                     closeWriterAndUpdatePathLocked(path);
82                 }
83 
84                 if (sWriter == null) {
85                     sWriter = Files.newBufferedWriter(path, CREATE, APPEND);
86                 }
87 
88                 sWriter.write(DATE_FORMAT.format(new Date()) + " " + msg + "\n");
89                 // Flush to guarantee that all our writes have been sent to the filesystem
90                 sWriter.flush();
91             } catch (IOException e) {
92                 closeWriterAndUpdatePathLocked(null);
93                 Log.w(TAG, "Failed to write: " + sPersistentFile, e);
94             }
95         }
96     }
97 
98     @GuardedBy("LOCK")
closeWriterAndUpdatePathLocked(@ullable Path newPath)99     private static void closeWriterAndUpdatePathLocked(@Nullable Path newPath) {
100         if (sWriter != null) {
101             try {
102                 sWriter.close();
103             } catch (IOException ignored) {
104                 Log.w(TAG, "Failed to close: " + sPersistentFile, ignored);
105             }
106             sWriter = null;
107         }
108         sPersistentFile = newPath;
109     }
110 
111     /**
112      * Dump any persistent logs.
113      */
dumpPersistent(@onNull PrintWriter pw)114     public static void dumpPersistent(@NonNull PrintWriter pw) {
115         Path persistentDir = null;
116         synchronized (LOCK) {
117             if (sPersistentDir == null) return;
118             persistentDir = sPersistentDir;
119         }
120 
121         try (Stream<Path> stream = Files.list(persistentDir)) {
122             stream.sorted().forEach((path) -> {
123                 dumpPersistentFile(path, pw);
124             });
125         } catch (IOException e) {
126             pw.println(e.getMessage());
127             pw.println();
128         }
129     }
130 
dumpPersistentFile(@onNull Path path, @NonNull PrintWriter pw)131     private static void dumpPersistentFile(@NonNull Path path, @NonNull PrintWriter pw) {
132         pw.println("Persistent logs in " + path + ":");
133         try (Stream<String> stream = Files.lines(path)) {
134             stream.forEach((line) -> {
135                 pw.println("  " + line);
136             });
137             pw.println();
138         } catch (IOException e) {
139             pw.println("  " + e.getMessage());
140             pw.println();
141         }
142     }
143 
144     /**
145      * Resolve the current log file to write new entries into. Automatically
146      * starts new files when the current file is larger than
147      * {@link #PERSISTENT_SIZE}.
148      */
149     @GuardedBy("LOCK")
resolveCurrentPersistentFileLocked()150     private static @NonNull Path resolveCurrentPersistentFileLocked() throws IOException {
151         if (sPersistentFile != null && sPersistentFile.toFile().length() < PERSISTENT_SIZE) {
152             return sPersistentFile;
153         }
154 
155         return sPersistentDir.resolve(String.valueOf(System.currentTimeMillis()));
156     }
157 }
158