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