Line data Source code
1 : // Copyright (C) 2019 The Android Open Source Project
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : package com.google.gerrit.server.logging;
16 :
17 : import com.google.auto.value.AutoValue;
18 : import com.google.common.base.MoreObjects;
19 : import com.google.common.base.MoreObjects.ToStringHelper;
20 : import com.google.common.collect.ImmutableList;
21 : import com.google.common.flogger.LazyArg;
22 : import com.google.common.flogger.LazyArgs;
23 : import com.google.gerrit.common.Nullable;
24 : import java.lang.reflect.InvocationTargetException;
25 : import java.lang.reflect.Method;
26 : import java.lang.reflect.Modifier;
27 : import java.util.Arrays;
28 : import java.util.Comparator;
29 : import java.util.Optional;
30 :
31 : /** Metadata that is provided to {@link PerformanceLogger}s as context for performance records. */
32 : @AutoValue
33 154 : public abstract class Metadata {
34 : /** The numeric ID of an account. */
35 : public abstract Optional<Integer> accountId();
36 :
37 : /**
38 : * The type of an action (ACCOUNT_UPDATE, CHANGE_UPDATE, GROUP_UPDATE, INDEX_QUERY,
39 : * PLUGIN_UPDATE).
40 : */
41 : public abstract Optional<String> actionType();
42 :
43 : /** An authentication domain name. */
44 : public abstract Optional<String> authDomainName();
45 :
46 : /** The name of a branch. */
47 : public abstract Optional<String> branchName();
48 :
49 : /** Key of an entity in a cache. */
50 : public abstract Optional<String> cacheKey();
51 :
52 : /** The name of a cache. */
53 : public abstract Optional<String> cacheName();
54 :
55 : /** The name of the implementation class. */
56 : public abstract Optional<String> className();
57 :
58 : /**
59 : * The reason of a request cancellation (CLIENT_CLOSED_REQUEST, CLIENT_PROVIDED_DEADLINE_EXCEEDED,
60 : * SERVER_DEADLINE_EXCEEDED).
61 : */
62 : public abstract Optional<String> cancellationReason();
63 :
64 : /** The numeric ID of a change. */
65 : public abstract Optional<Integer> changeId();
66 :
67 : /**
68 : * The type of change ID which the user used to identify a change (e.g. numeric ID, triplet etc.).
69 : */
70 : public abstract Optional<String> changeIdType();
71 :
72 : /** The cause of an error. */
73 : public abstract Optional<String> cause();
74 :
75 : /** Side where the comment is written: <= 0 for parent, 1 for revision. */
76 : public abstract Optional<Integer> commentSide();
77 :
78 : /** The SHA1 of a commit. */
79 : public abstract Optional<String> commit();
80 :
81 : /** Diff algorithm used in diff computation. */
82 : public abstract Optional<String> diffAlgorithm();
83 :
84 : /** The type of an event. */
85 : public abstract Optional<String> eventType();
86 :
87 : /** The value of the @Export annotation which was used to register a plugin extension. */
88 : public abstract Optional<String> exportValue();
89 :
90 : /** Path of a file in a repository. */
91 : public abstract Optional<String> filePath();
92 :
93 : /** Garbage collector name. */
94 : public abstract Optional<String> garbageCollectorName();
95 :
96 : /** Git operation (CLONE, FETCH). */
97 : public abstract Optional<String> gitOperation();
98 :
99 : /** The numeric ID of an internal group. */
100 : public abstract Optional<Integer> groupId();
101 :
102 : /** The name of a group. */
103 : public abstract Optional<String> groupName();
104 :
105 : /** The group system being queried. */
106 : public abstract Optional<String> groupSystem();
107 :
108 : /** The UUID of a group. */
109 : public abstract Optional<String> groupUuid();
110 :
111 : /** HTTP status response code. */
112 : public abstract Optional<Integer> httpStatus();
113 :
114 : /** The name of a secondary index. */
115 : public abstract Optional<String> indexName();
116 :
117 : /** The version of a secondary index. */
118 : public abstract Optional<Integer> indexVersion();
119 :
120 : /** The name of the implementation method. */
121 : public abstract Optional<String> memoryPoolName();
122 :
123 : /** The name of the implementation method. */
124 : public abstract Optional<String> methodName();
125 :
126 : /** One or more resources */
127 : public abstract Optional<Boolean> multiple();
128 :
129 : /** The name of an operation that is performed. */
130 : public abstract Optional<String> operationName();
131 :
132 : /** Partial or full computation */
133 : public abstract Optional<Boolean> partial();
134 :
135 : /** If a value is still current or not */
136 : public abstract Optional<Boolean> outdated();
137 :
138 : /** Path of a metadata file in NoteDb. */
139 : public abstract Optional<String> noteDbFilePath();
140 :
141 : /** Name of a metadata ref in NoteDb. */
142 : public abstract Optional<String> noteDbRefName();
143 :
144 : /** Type of a sequence in NoteDb (ACCOUNTS, CHANGES, GROUPS). */
145 : public abstract Optional<String> noteDbSequenceType();
146 :
147 : /** The ID of a patch set. */
148 : public abstract Optional<Integer> patchSetId();
149 :
150 : /** Plugin metadata that doesn't fit into any other category. */
151 : public abstract ImmutableList<PluginMetadata> pluginMetadata();
152 :
153 : /** The name of a plugin. */
154 : public abstract Optional<String> pluginName();
155 :
156 : /** The name of a Gerrit project (aka Git repository). */
157 : public abstract Optional<String> projectName();
158 :
159 : /** The type of a Git push to Gerrit (CREATE_REPLACE, NORMAL, AUTOCLOSE). */
160 : public abstract Optional<String> pushType();
161 :
162 : /** The type of a Git push to Gerrit (GIT_RECEIVE, GIT_UPLOAD, REST, SSH). */
163 : public abstract Optional<String> requestType();
164 :
165 : /** The number of resources that is processed. */
166 : public abstract Optional<Integer> resourceCount();
167 :
168 : /** The name of a REST view. */
169 : public abstract Optional<String> restViewName();
170 :
171 : public abstract Optional<String> submitRequirementName();
172 :
173 : /** The SHA1 of Git commit. */
174 : public abstract Optional<String> revision();
175 :
176 : /** The username of an account. */
177 : public abstract Optional<String> username();
178 :
179 : /**
180 : * Returns a string representation of this instance that is suitable for logging. This is wrapped
181 : * in a {@link LazyArg} because it is expensive to evaluate.
182 : *
183 : * <p>{@link #toString()} formats the {@link Optional} fields as {@code key=Optional[value]} or
184 : * {@code key=Optional.empty}. Since this class has many optional fields from which usually only a
185 : * few are populated this leads to long string representations such as
186 : *
187 : * <pre>
188 : * Metadata{accountId=Optional.empty, actionType=Optional.empty, authDomainName=Optional.empty,
189 : * branchName=Optional.empty, cacheKey=Optional.empty, cacheName=Optional.empty,
190 : * className=Optional.empty, cancellationReason=Optional.empty changeId=Optional[9212550],
191 : * changeIdType=Optional.empty, cause=Optional.empty, diffAlgorithm=Optional.empty,
192 : * eventType=Optional.empty, exportValue=Optional.empty, filePath=Optional.empty,
193 : * garbageCollectorName=Optional.empty, gitOperation=Optional.empty, groupId=Optional.empty,
194 : * groupName=Optional.empty, groupUuid=Optional.empty, httpStatus=Optional.empty,
195 : * indexName=Optional.empty, indexVersion=Optional[0], methodName=Optional.empty,
196 : * multiple=Optional.empty, operationName=Optional.empty, partial=Optional.empty,
197 : * noteDbFilePath=Optional.empty, noteDbRefName=Optional.empty,
198 : * noteDbSequenceType=Optional.empty, patchSetId=Optional.empty, pluginMetadata=[],
199 : * pluginName=Optional.empty, projectName=Optional.empty, pushType=Optional.empty,
200 : * requestType=Optional.empty, resourceCount=Optional.empty, restViewName=Optional.empty,
201 : * revision=Optional.empty, username=Optional.empty}
202 : * </pre>
203 : *
204 : * <p>That's hard to read in logs. This is why this method
205 : *
206 : * <ul>
207 : * <li>drops fields which have {@code Optional.empty} as value and
208 : * <li>reformats values that are {@code Optional[value]} to {@code value}.
209 : * </ul>
210 : *
211 : * <p>For the example given above the formatted string would look like this:
212 : *
213 : * <pre>
214 : * Metadata{changeId=9212550, indexVersion=0, pluginMetadata=[]}
215 : * </pre>
216 : *
217 : * @return string representation of this instance that is suitable for logging
218 : */
219 : LazyArg<String> toStringForLoggingLazy() {
220 : // Don't use a lambda because different compilers generate different method names for lambdas,
221 : // e.g. "lambda$myFunction$0" vs. just "lambda$0" in Eclipse. We need to identify the method
222 : // by name to skip it and avoid infinite recursion.
223 154 : return LazyArgs.lazy(this::toStringForLoggingImpl);
224 : }
225 :
226 : private String toStringForLoggingImpl() {
227 : // Append class name.
228 30 : String className = getClass().getSimpleName();
229 30 : if (className.startsWith("AutoValue_")) {
230 30 : className = className.substring(10);
231 : }
232 30 : ToStringHelper stringHelper = MoreObjects.toStringHelper(className);
233 :
234 : // Append key-value pairs for field which are set.
235 30 : Method[] methods = Metadata.class.getDeclaredMethods();
236 30 : Arrays.sort(methods, Comparator.comparing(Method::getName));
237 30 : for (Method method : methods) {
238 30 : if (Modifier.isStatic(method.getModifiers())) {
239 : // skip static method
240 30 : continue;
241 : }
242 :
243 30 : if (method.getName().equals("toStringForLoggingLazy")
244 30 : || method.getName().equals("toStringForLoggingImpl")) {
245 : // Don't call myself in infinite recursion.
246 30 : continue;
247 : }
248 :
249 30 : if (method.getReturnType().equals(Void.TYPE) || method.getParameterCount() > 0) {
250 : // skip method since it's not a getter
251 0 : continue;
252 : }
253 :
254 30 : method.setAccessible(true);
255 :
256 : Object returnValue;
257 : try {
258 30 : returnValue = method.invoke(this);
259 0 : } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
260 : // should never happen
261 0 : throw new IllegalStateException(e);
262 30 : }
263 :
264 30 : if (returnValue instanceof Optional) {
265 30 : Optional<?> fieldValueOptional = (Optional<?>) returnValue;
266 30 : if (!fieldValueOptional.isPresent()) {
267 : // drop this key-value pair
268 30 : continue;
269 : }
270 :
271 : // format as 'key=value' instead of 'key=Optional[value]'
272 30 : stringHelper.add(method.getName(), fieldValueOptional.get());
273 30 : } else {
274 : // not an Optional value, keep as is
275 30 : stringHelper.add(method.getName(), returnValue);
276 : }
277 : }
278 :
279 30 : return stringHelper.toString();
280 : }
281 :
282 : public static Metadata.Builder builder() {
283 154 : return new AutoValue_Metadata.Builder();
284 : }
285 :
286 : public static Metadata empty() {
287 110 : return builder().build();
288 : }
289 :
290 : @AutoValue.Builder
291 154 : public abstract static class Builder {
292 : public abstract Builder accountId(int accountId);
293 :
294 : public abstract Builder actionType(@Nullable String actionType);
295 :
296 : public abstract Builder authDomainName(@Nullable String authDomainName);
297 :
298 : public abstract Builder branchName(@Nullable String branchName);
299 :
300 : public abstract Builder cacheKey(@Nullable String cacheKey);
301 :
302 : public abstract Builder cacheName(@Nullable String cacheName);
303 :
304 : public abstract Builder className(@Nullable String className);
305 :
306 : public abstract Builder cancellationReason(@Nullable String cancellationReason);
307 :
308 : public abstract Builder changeId(int changeId);
309 :
310 : public abstract Builder changeIdType(@Nullable String changeIdType);
311 :
312 : public abstract Builder cause(@Nullable String cause);
313 :
314 : public abstract Builder commentSide(int side);
315 :
316 : public abstract Builder commit(@Nullable String commit);
317 :
318 : public abstract Builder diffAlgorithm(@Nullable String diffAlgorithm);
319 :
320 : public abstract Builder eventType(@Nullable String eventType);
321 :
322 : public abstract Builder exportValue(@Nullable String exportValue);
323 :
324 : public abstract Builder filePath(@Nullable String filePath);
325 :
326 : public abstract Builder garbageCollectorName(@Nullable String garbageCollectorName);
327 :
328 : public abstract Builder gitOperation(@Nullable String gitOperation);
329 :
330 : public abstract Builder groupId(int groupId);
331 :
332 : public abstract Builder groupName(@Nullable String groupName);
333 :
334 : public abstract Builder groupSystem(@Nullable String groupSystem);
335 :
336 : public abstract Builder groupUuid(@Nullable String groupUuid);
337 :
338 : public abstract Builder httpStatus(int httpStatus);
339 :
340 : public abstract Builder indexName(@Nullable String indexName);
341 :
342 : public abstract Builder indexVersion(int indexVersion);
343 :
344 : public abstract Builder memoryPoolName(@Nullable String memoryPoolName);
345 :
346 : public abstract Builder methodName(@Nullable String methodName);
347 :
348 : public abstract Builder multiple(boolean multiple);
349 :
350 : public abstract Builder operationName(String operationName);
351 :
352 : public abstract Builder partial(boolean partial);
353 :
354 : public abstract Builder outdated(boolean outdated);
355 :
356 : public abstract Builder noteDbFilePath(@Nullable String noteDbFilePath);
357 :
358 : public abstract Builder noteDbRefName(@Nullable String noteDbRefName);
359 :
360 : public abstract Builder noteDbSequenceType(@Nullable String noteDbSequenceType);
361 :
362 : public abstract Builder patchSetId(int patchSetId);
363 :
364 : abstract ImmutableList.Builder<PluginMetadata> pluginMetadataBuilder();
365 :
366 : public Builder addPluginMetadata(PluginMetadata pluginMetadata) {
367 0 : pluginMetadataBuilder().add(pluginMetadata);
368 0 : return this;
369 : }
370 :
371 : public abstract Builder pluginName(@Nullable String pluginName);
372 :
373 : public abstract Builder projectName(@Nullable String projectName);
374 :
375 : public abstract Builder pushType(@Nullable String pushType);
376 :
377 : public abstract Builder requestType(@Nullable String requestType);
378 :
379 : public abstract Builder resourceCount(int resourceCount);
380 :
381 : public abstract Builder restViewName(@Nullable String restViewName);
382 :
383 : public abstract Builder revision(@Nullable String revision);
384 :
385 : public abstract Builder submitRequirementName(@Nullable String srName);
386 :
387 : public abstract Builder username(@Nullable String username);
388 :
389 : public abstract Metadata build();
390 : }
391 : }
|