| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.ddmlib.logcat; |
| |
| import com.android.annotations.NonNull; |
| import com.android.ddmlib.IDevice; |
| import com.android.ddmlib.Log.LogLevel; |
| import com.google.common.primitives.Ints; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| /** |
| * Class to parse raw output of {@code adb logcat -v long} to {@link LogCatMessage} objects. |
| */ |
| public final class LogCatMessageParser { |
| private LogLevel mCurLogLevel = LogLevel.WARN; |
| private String mCurPid = "?"; |
| private String mCurTid = "?"; |
| private String mCurTag = "?"; |
| private String mCurTime = "?:??"; |
| |
| /** |
| * This pattern is meant to parse the first line of a log message with the option |
| * 'logcat -v long'. The first line represents the date, tag, severity, etc.. while the |
| * following lines are the message (can be several lines).<br> |
| * This first line looks something like:<br> |
| * {@code "[ 00-00 00:00:00.000 <pid>:0x<???> <severity>/<tag>]"} |
| * <br> |
| * Note: severity is one of V, D, I, W, E, A? or F. However, there doesn't seem to be |
| * a way to actually generate an A (assert) message. Log.wtf is supposed to generate |
| * a message with severity A, however it generates the undocumented F level. In |
| * such a case, the parser will change the level from F to A.<br> |
| * Note: the fraction of second value can have any number of digit.<br> |
| * Note: the tag should be trimmed as it may have spaces at the end. |
| */ |
| private static final Pattern sLogHeaderPattern = Pattern.compile( |
| "^\\[\\s(\\d\\d-\\d\\d\\s\\d\\d:\\d\\d:\\d\\d\\.\\d+)" |
| + "\\s+(\\d*):\\s*(\\S+)\\s([VDIWEAF])/(.*)\\]$"); |
| |
| /** |
| * Parse a list of strings into {@link LogCatMessage} objects. This method |
| * maintains state from previous calls regarding the last seen header of |
| * logcat messages. |
| * @param lines list of raw strings obtained from logcat -v long |
| * @param device device from which these log messages have been received |
| * @return list of LogMessage objects parsed from the input |
| */ |
| @NonNull |
| public List<LogCatMessage> processLogLines(String[] lines, IDevice device) { |
| List<LogCatMessage> messages = new ArrayList<LogCatMessage>(lines.length); |
| |
| for (String line : lines) { |
| if (line.isEmpty()) { |
| continue; |
| } |
| |
| Matcher matcher = sLogHeaderPattern.matcher(line); |
| if (matcher.matches()) { |
| mCurTime = matcher.group(1); |
| mCurPid = matcher.group(2); |
| mCurTid = matcher.group(3); |
| mCurLogLevel = LogLevel.getByLetterString(matcher.group(4)); |
| mCurTag = matcher.group(5).trim(); |
| |
| /* LogLevel doesn't support messages with severity "F". Log.wtf() is supposed |
| * to generate "A", but generates "F". */ |
| if (mCurLogLevel == null && matcher.group(4).equals("F")) { |
| mCurLogLevel = LogLevel.ASSERT; |
| } |
| } else { |
| String pkgName = ""; //$NON-NLS-1$ |
| Integer pid = Ints.tryParse(mCurPid); |
| if (pid != null && device != null) { |
| pkgName = device.getClientName(pid); |
| } |
| LogCatMessage m = new LogCatMessage(mCurLogLevel, mCurPid, mCurTid, |
| pkgName, mCurTag, mCurTime, line); |
| messages.add(m); |
| } |
| } |
| |
| return messages; |
| } |
| } |