Skip to content

Commit f2ab0fd

Browse files
hagbardFlogger Team
authored andcommitted
Adding provisional support for metadata keys/values in scopes.
RELNOTES=Adding provisional support for metadata keys/values in scopes. PiperOrigin-RevId: 342716796
1 parent bdd3a42 commit f2ab0fd

File tree

4 files changed

+528
-45
lines changed

4 files changed

+528
-45
lines changed
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
/*
2+
* Copyright (C) 2020 The Flogger Authors.
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+
17+
package com.google.common.flogger.context;
18+
19+
import static com.google.common.flogger.util.Checks.checkArgument;
20+
import static com.google.common.flogger.util.Checks.checkNotNull;
21+
22+
import com.google.common.flogger.MetadataKey;
23+
import com.google.common.flogger.backend.Metadata;
24+
import java.util.ArrayList;
25+
import java.util.Arrays;
26+
import java.util.List;
27+
import org.checkerframework.checker.nullness.compatqual.NullableDecl;
28+
29+
/**
30+
* Immutable {@link Metadata} implementation intended for use in nested scopes. Scope metadata can
31+
* be concatenated to inherit metadata from a parent scope. This class is only expected to be needed
32+
* by implementations of {@link ScopedLoggingContext} and should not be considered a stable API.
33+
*/
34+
public abstract class ScopeMetadata extends Metadata {
35+
private static final class Entry<T> {
36+
final MetadataKey<T> key;
37+
final T value;
38+
39+
Entry(MetadataKey<T> key, T value) {
40+
this.key = checkNotNull(key, "key");
41+
this.value = checkNotNull(value, "value");
42+
}
43+
}
44+
45+
/**
46+
* A builder to collect metadata key/values pairs in order. This class is only expected to be
47+
* needed by implementations of {@link ScopedLoggingContext} and should not be considered a stable
48+
* API.
49+
*/
50+
public static final class Builder {
51+
private static final Entry<?>[] EMPTY_ARRAY = new Entry<?>[0];
52+
53+
// Set an explicitly small initial capacity to avoid excessive allocations when we only ever
54+
// expect one or two keys to be added per scope. We don't optimize for the case of zero keys,
55+
// since the scoped context builder shouldn't create a builder until the first key is added.
56+
private final List<Entry<?>> entries = new ArrayList<Entry<?>>(2);
57+
58+
private Builder() {}
59+
60+
/** Add a single metadata key/value pair to the builder. */
61+
public <T> Builder add(MetadataKey<T> key, T value) {
62+
// Entries are immutable and get moved into the metadata when it's built, so these get shared
63+
// and reduce the size of the metadata storage compared to storing adjacent key/value pairs.
64+
entries.add(new Entry<T>(key, value));
65+
return this;
66+
}
67+
68+
public ScopeMetadata build() {
69+
// Analysis shows it's quicker to pass an empty array here and let the JVM optimize to avoid
70+
// creating an empty array just to overwrite all its elements.
71+
return new ImmutableScopeMetadata(entries.toArray(EMPTY_ARRAY));
72+
}
73+
}
74+
75+
/** Returns a new {@code ScopeMetadata} builder. */
76+
public static Builder builder() {
77+
return new Builder();
78+
}
79+
80+
/** Returns a space efficient {@code ScopeMetadata} containing a single value. */
81+
public static <T> ScopeMetadata singleton(MetadataKey<T> key, T value) {
82+
return new SingletonMetadata(key, value);
83+
}
84+
85+
/** Returns the empty {@code ScopeMetadata}. */
86+
// We can't use empty() here as that's already taken by Metadata.
87+
public static ScopeMetadata none() {
88+
return EmptyMetadata.INSTANCE;
89+
}
90+
91+
private ScopeMetadata() {}
92+
93+
/**
94+
* Concatenates the given scope metadata <em>after</em> this instance. Key value pairs are simply
95+
* concatenated (rather than being merged) which may result in multiple single valued keys
96+
* existing in the resulting sequence.
97+
*
98+
* <p>Whether this is achieved via copying or chaining of instances is an implementation detail.
99+
*
100+
* <p>Use {@link com.google.common.flogger.backend.MetadataProcessor MetadataProcessor} to process
101+
* metadata consistently with respect to single valued and repeated keys, and use {@link
102+
* Metadata#findValue(MetadataKey)} to lookup the "most recent" value for a single valued key.
103+
*/
104+
public abstract ScopeMetadata concatenate(ScopeMetadata metadata);
105+
106+
// Internal method to deal in entries directly during concatenation.
107+
abstract Entry<?> get(int n);
108+
109+
@Override
110+
public MetadataKey<?> getKey(int n) {
111+
return get(n).key;
112+
}
113+
114+
@Override
115+
public Object getValue(int n) {
116+
return get(n).value;
117+
}
118+
119+
private static final class ImmutableScopeMetadata extends ScopeMetadata {
120+
private final Entry<?>[] entries;
121+
122+
ImmutableScopeMetadata(Entry<?>[] entries) {
123+
this.entries = entries;
124+
}
125+
126+
@Override
127+
public int size() {
128+
return entries.length;
129+
}
130+
131+
@Override
132+
Entry<?> get(int n) {
133+
return entries[n];
134+
}
135+
136+
@Override
137+
@NullableDecl
138+
@SuppressWarnings("unchecked")
139+
public <T> T findValue(MetadataKey<T> key) {
140+
checkArgument(!key.canRepeat(), "metadata key must be single valued");
141+
for (int n = entries.length - 1; n >= 0; n--) {
142+
Entry<?> e = entries[n];
143+
if (e.key.equals(key)) {
144+
return (T) e.value;
145+
}
146+
}
147+
return null;
148+
}
149+
150+
@Override
151+
public ScopeMetadata concatenate(ScopeMetadata metadata) {
152+
int extraSize = metadata.size();
153+
if (extraSize == 0) {
154+
return this;
155+
}
156+
if (entries.length == 0) {
157+
return metadata;
158+
}
159+
Entry<?>[] merged = Arrays.copyOf(entries, entries.length + extraSize);
160+
for (int i = 0; i < extraSize; i++) {
161+
merged[i + entries.length] = metadata.get(i);
162+
}
163+
return new ImmutableScopeMetadata(merged);
164+
}
165+
}
166+
167+
private static final class SingletonMetadata extends ScopeMetadata {
168+
private final Entry<?> entry;
169+
170+
<T> SingletonMetadata(MetadataKey<T> key, T value) {
171+
this.entry = new Entry<T>(key, value);
172+
}
173+
174+
@Override
175+
public int size() {
176+
return 1;
177+
}
178+
179+
@Override
180+
Entry<?> get(int n) {
181+
if (n == 0) {
182+
return entry;
183+
}
184+
throw new IndexOutOfBoundsException();
185+
}
186+
187+
@Override
188+
@NullableDecl
189+
@SuppressWarnings("unchecked")
190+
public <R> R findValue(MetadataKey<R> key) {
191+
checkArgument(!key.canRepeat(), "metadata key must be single valued");
192+
return entry.key.equals(key) ? (R) entry.value : null;
193+
}
194+
195+
@Override
196+
public ScopeMetadata concatenate(ScopeMetadata metadata) {
197+
// No check for size() == 0 since this instance always has one value.
198+
int extraSize = metadata.size();
199+
if (extraSize == 0) {
200+
return this;
201+
}
202+
Entry<?>[] merged = new Entry<?>[extraSize + 1];
203+
merged[0] = entry;
204+
for (int i = 0; i < extraSize; i++) {
205+
merged[i + 1] = metadata.get(i);
206+
}
207+
return new ImmutableScopeMetadata(merged);
208+
}
209+
}
210+
211+
// This is a static nested class as opposed to an anonymous class assigned to a constant field in
212+
// order to decouple its classloading when Metadata is loaded. Android users are particularly
213+
// careful about unnecessary class loading, and we've used similar mechanisms in Guava (see
214+
// CharMatchers).
215+
private static final class EmptyMetadata extends ScopeMetadata {
216+
static final ScopeMetadata INSTANCE = new EmptyMetadata();
217+
218+
@Override
219+
public int size() {
220+
return 0;
221+
}
222+
223+
@Override
224+
Entry<?> get(int n) {
225+
throw new IndexOutOfBoundsException();
226+
}
227+
228+
@Override
229+
@NullableDecl
230+
public <T> T findValue(MetadataKey<T> key) {
231+
// For consistency, do the same checks as for non-empty instances.
232+
checkArgument(!key.canRepeat(), "metadata key must be single valued");
233+
return null;
234+
}
235+
236+
@Override
237+
public ScopeMetadata concatenate(ScopeMetadata metadata) {
238+
return metadata;
239+
}
240+
}
241+
}

0 commit comments

Comments
 (0)