GenerateLinksLogger.java revision 5a03094ebc91df1c64a2232be648ac3ed26657ce
1/* 2 * Copyright (C) 2017 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 17package android.view.textclassifier; 18 19import android.annotation.Nullable; 20import android.metrics.LogMaker; 21import android.util.ArrayMap; 22 23import com.android.internal.annotations.VisibleForTesting; 24import com.android.internal.logging.MetricsLogger; 25import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 26import com.android.internal.util.Preconditions; 27 28import java.util.Locale; 29import java.util.Map; 30import java.util.Objects; 31import java.util.Random; 32import java.util.UUID; 33 34/** 35 * A helper for logging calls to generateLinks. 36 * @hide 37 */ 38@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 39public final class GenerateLinksLogger { 40 41 private static final String LOG_TAG = "GenerateLinksLogger"; 42 private static final boolean DEBUG_LOG_ENABLED = false; 43 private static final String ZERO = "0"; 44 45 private final MetricsLogger mMetricsLogger; 46 private final Random mRng; 47 private final int mSampleRate; 48 49 /** 50 * @param sampleRate the rate at which log events are written. (e.g. 100 means there is a 0.01 51 * chance that a call to logGenerateLinks results in an event being written). 52 * To write all events, pass 1. 53 */ 54 public GenerateLinksLogger(int sampleRate) { 55 mSampleRate = sampleRate; 56 mRng = new Random(System.nanoTime()); 57 mMetricsLogger = new MetricsLogger(); 58 } 59 60 @VisibleForTesting 61 public GenerateLinksLogger(int sampleRate, MetricsLogger metricsLogger) { 62 mSampleRate = sampleRate; 63 mRng = new Random(System.nanoTime()); 64 mMetricsLogger = metricsLogger; 65 } 66 67 /** Logs statistics about a call to generateLinks. */ 68 public void logGenerateLinks(CharSequence text, TextLinks links, String callingPackageName, 69 long latencyMs) { 70 Preconditions.checkNotNull(text); 71 Preconditions.checkNotNull(links); 72 Preconditions.checkNotNull(callingPackageName); 73 if (!shouldLog()) { 74 return; 75 } 76 77 // Always populate the total stats, and per-entity stats for each entity type detected. 78 final LinkifyStats totalStats = new LinkifyStats(); 79 final Map<String, LinkifyStats> perEntityTypeStats = new ArrayMap<>(); 80 for (TextLinks.TextLink link : links.getLinks()) { 81 if (link.getEntityCount() == 0) continue; 82 final String entityType = link.getEntity(0); 83 if (entityType == null 84 || TextClassifier.TYPE_OTHER.equals(entityType) 85 || TextClassifier.TYPE_UNKNOWN.equals(entityType)) { 86 continue; 87 } 88 totalStats.countLink(link); 89 perEntityTypeStats.computeIfAbsent(entityType, k -> new LinkifyStats()).countLink(link); 90 } 91 92 final String callId = UUID.randomUUID().toString(); 93 writeStats(callId, callingPackageName, null, totalStats, text, latencyMs); 94 for (Map.Entry<String, LinkifyStats> entry : perEntityTypeStats.entrySet()) { 95 writeStats(callId, callingPackageName, entry.getKey(), entry.getValue(), text, 96 latencyMs); 97 } 98 } 99 100 /** 101 * Returns whether this particular event should be logged. 102 * 103 * Sampling is used to reduce the amount of logging data generated. 104 **/ 105 private boolean shouldLog() { 106 if (mSampleRate <= 1) { 107 return true; 108 } else { 109 return mRng.nextInt(mSampleRate) == 0; 110 } 111 } 112 113 /** Writes a log event for the given stats. */ 114 private void writeStats(String callId, String callingPackageName, @Nullable String entityType, 115 LinkifyStats stats, CharSequence text, long latencyMs) { 116 final LogMaker log = new LogMaker(MetricsEvent.TEXT_CLASSIFIER_GENERATE_LINKS) 117 .setPackageName(callingPackageName) 118 .addTaggedData(MetricsEvent.FIELD_LINKIFY_CALL_ID, callId) 119 .addTaggedData(MetricsEvent.FIELD_LINKIFY_NUM_LINKS, stats.mNumLinks) 120 .addTaggedData(MetricsEvent.FIELD_LINKIFY_LINK_LENGTH, stats.mNumLinksTextLength) 121 .addTaggedData(MetricsEvent.FIELD_LINKIFY_TEXT_LENGTH, text.length()) 122 .addTaggedData(MetricsEvent.FIELD_LINKIFY_LATENCY, latencyMs); 123 if (entityType != null) { 124 log.addTaggedData(MetricsEvent.FIELD_LINKIFY_ENTITY_TYPE, entityType); 125 } 126 mMetricsLogger.write(log); 127 debugLog(log); 128 } 129 130 private static void debugLog(LogMaker log) { 131 if (!DEBUG_LOG_ENABLED) return; 132 133 final String callId = Objects.toString( 134 log.getTaggedData(MetricsEvent.FIELD_LINKIFY_CALL_ID), ""); 135 final String entityType = Objects.toString( 136 log.getTaggedData(MetricsEvent.FIELD_LINKIFY_ENTITY_TYPE), "ANY_ENTITY"); 137 final int numLinks = Integer.parseInt( 138 Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_NUM_LINKS), ZERO)); 139 final int linkLength = Integer.parseInt( 140 Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_LINK_LENGTH), ZERO)); 141 final int textLength = Integer.parseInt( 142 Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_TEXT_LENGTH), ZERO)); 143 final int latencyMs = Integer.parseInt( 144 Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_LATENCY), ZERO)); 145 146 Log.d(LOG_TAG, 147 String.format(Locale.US, "%s:%s %d links (%d/%d chars) %dms %s", callId, entityType, 148 numLinks, linkLength, textLength, latencyMs, log.getPackageName())); 149 } 150 151 /** Helper class for storing per-entity type statistics. */ 152 private static final class LinkifyStats { 153 int mNumLinks; 154 int mNumLinksTextLength; 155 156 void countLink(TextLinks.TextLink link) { 157 mNumLinks += 1; 158 mNumLinksTextLength += link.getEnd() - link.getStart(); 159 } 160 } 161} 162