blob: 3996b277aa1c1524b317e3eb0eacdfcffb218ff4 [file] [log] [blame]
Jan Althaus31efdc32018-02-19 22:23:13 +01001/*
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
Abodunrinwa Tokif1d93992018-03-02 13:53:21 +000017package android.view.textclassifier;
Jan Althaus31efdc32018-02-19 22:23:13 +010018
19import android.annotation.Nullable;
20import android.metrics.LogMaker;
21import android.util.ArrayMap;
Jan Althaus31efdc32018-02-19 22:23:13 +010022
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
Jan Althaus5a030942018-04-04 19:40:38 +020028import java.util.Locale;
Jan Althaus31efdc32018-02-19 22:23:13 +010029import 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 String ZERO = "0";
43
44 private final MetricsLogger mMetricsLogger;
45 private final Random mRng;
46 private final int mSampleRate;
47
48 /**
49 * @param sampleRate the rate at which log events are written. (e.g. 100 means there is a 0.01
50 * chance that a call to logGenerateLinks results in an event being written).
51 * To write all events, pass 1.
52 */
53 public GenerateLinksLogger(int sampleRate) {
54 mSampleRate = sampleRate;
55 mRng = new Random(System.nanoTime());
56 mMetricsLogger = new MetricsLogger();
57 }
58
59 @VisibleForTesting
60 public GenerateLinksLogger(int sampleRate, MetricsLogger metricsLogger) {
61 mSampleRate = sampleRate;
62 mRng = new Random(System.nanoTime());
63 mMetricsLogger = metricsLogger;
64 }
65
66 /** Logs statistics about a call to generateLinks. */
67 public void logGenerateLinks(CharSequence text, TextLinks links, String callingPackageName,
68 long latencyMs) {
69 Preconditions.checkNotNull(text);
70 Preconditions.checkNotNull(links);
71 Preconditions.checkNotNull(callingPackageName);
72 if (!shouldLog()) {
73 return;
74 }
75
76 // Always populate the total stats, and per-entity stats for each entity type detected.
77 final LinkifyStats totalStats = new LinkifyStats();
78 final Map<String, LinkifyStats> perEntityTypeStats = new ArrayMap<>();
79 for (TextLinks.TextLink link : links.getLinks()) {
80 if (link.getEntityCount() == 0) continue;
81 final String entityType = link.getEntity(0);
82 if (entityType == null
83 || TextClassifier.TYPE_OTHER.equals(entityType)
84 || TextClassifier.TYPE_UNKNOWN.equals(entityType)) {
85 continue;
86 }
87 totalStats.countLink(link);
88 perEntityTypeStats.computeIfAbsent(entityType, k -> new LinkifyStats()).countLink(link);
89 }
90
91 final String callId = UUID.randomUUID().toString();
92 writeStats(callId, callingPackageName, null, totalStats, text, latencyMs);
93 for (Map.Entry<String, LinkifyStats> entry : perEntityTypeStats.entrySet()) {
94 writeStats(callId, callingPackageName, entry.getKey(), entry.getValue(), text,
95 latencyMs);
96 }
97 }
98
99 /**
100 * Returns whether this particular event should be logged.
101 *
102 * Sampling is used to reduce the amount of logging data generated.
103 **/
104 private boolean shouldLog() {
105 if (mSampleRate <= 1) {
106 return true;
107 } else {
108 return mRng.nextInt(mSampleRate) == 0;
109 }
110 }
111
112 /** Writes a log event for the given stats. */
113 private void writeStats(String callId, String callingPackageName, @Nullable String entityType,
114 LinkifyStats stats, CharSequence text, long latencyMs) {
115 final LogMaker log = new LogMaker(MetricsEvent.TEXT_CLASSIFIER_GENERATE_LINKS)
116 .setPackageName(callingPackageName)
117 .addTaggedData(MetricsEvent.FIELD_LINKIFY_CALL_ID, callId)
118 .addTaggedData(MetricsEvent.FIELD_LINKIFY_NUM_LINKS, stats.mNumLinks)
119 .addTaggedData(MetricsEvent.FIELD_LINKIFY_LINK_LENGTH, stats.mNumLinksTextLength)
120 .addTaggedData(MetricsEvent.FIELD_LINKIFY_TEXT_LENGTH, text.length())
121 .addTaggedData(MetricsEvent.FIELD_LINKIFY_LATENCY, latencyMs);
122 if (entityType != null) {
123 log.addTaggedData(MetricsEvent.FIELD_LINKIFY_ENTITY_TYPE, entityType);
124 }
125 mMetricsLogger.write(log);
126 debugLog(log);
127 }
128
129 private static void debugLog(LogMaker log) {
Tony Makafd54672019-01-04 15:56:44 +0000130 if (!Log.ENABLE_FULL_LOGGING) {
131 return;
132 }
Jan Althaus31efdc32018-02-19 22:23:13 +0100133 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
Tony Makafd54672019-01-04 15:56:44 +0000146 Log.v(LOG_TAG,
Jan Althaus5a030942018-04-04 19:40:38 +0200147 String.format(Locale.US, "%s:%s %d links (%d/%d chars) %dms %s", callId, entityType,
148 numLinks, linkLength, textLength, latencyMs, log.getPackageName()));
Jan Althaus31efdc32018-02-19 22:23:13 +0100149 }
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}