/
StringListMetricType.kt
183 lines (166 loc) · 6.57 KB
/
StringListMetricType.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package mozilla.telemetry.glean.private
import androidx.annotation.VisibleForTesting
import com.sun.jna.StringArray
import mozilla.telemetry.glean.Dispatchers
import mozilla.telemetry.glean.rust.LibGleanFFI
import mozilla.telemetry.glean.rust.getAndConsumeRustString
import mozilla.telemetry.glean.rust.toBoolean
import mozilla.telemetry.glean.rust.toByte
import mozilla.telemetry.glean.testing.ErrorType
import mozilla.telemetry.glean.utils.toList
import org.json.JSONArray
/**
* This implements the developer facing API for recording string list metrics.
*
* Instances of this class type are automatically generated by the parsers at build time,
* allowing developers to record values that were previously registered in the metrics.yaml file.
*
* The string list API only exposes the [add] and [set] methods, which takes care of validating the input
* data and making sure that limits are enforced.
*
* The internal constructor is only used by [LabeledMetricType] directly.
*/
class StringListMetricType(
private var handle: Long,
private val disabled: Boolean,
private val sendInPings: List<String>
) {
/**
* The public constructor used by automatically-generated metrics.
*/
constructor(
disabled: Boolean,
category: String,
lifetime: Lifetime,
name: String,
sendInPings: List<String>
) : this(handle = 0, disabled = disabled, sendInPings = sendInPings) {
val ffiPingsList = StringArray(sendInPings.toTypedArray(), "utf-8")
this.handle = LibGleanFFI.INSTANCE.glean_new_string_list_metric(
category = category,
name = name,
send_in_pings = ffiPingsList,
send_in_pings_len = sendInPings.size,
lifetime = lifetime.ordinal,
disabled = disabled.toByte())
}
/**
* Appends a string value to one or more string list metric stores. If the string exceeds the
* maximum string length or if the list exceeds the maximum length it will be truncated.
*
* @param value This is a user defined string value.
*/
fun add(value: String) {
if (disabled) {
return
}
@Suppress("EXPERIMENTAL_API_USAGE")
Dispatchers.API.launch {
LibGleanFFI.INSTANCE.glean_string_list_add(
this@StringListMetricType.handle,
value)
}
}
/**
* Sets a string list to one or more metric stores. If any string exceeds the maximum string
* length or if the list exceeds the maximum length it will be truncated.
*
* @param value This is a user defined string list.
*/
fun set(value: List<String>) {
if (disabled) {
return
}
val ffiValueList = StringArray(value.toTypedArray(), "utf-8")
@Suppress("EXPERIMENTAL_API_USAGE")
Dispatchers.API.launch {
LibGleanFFI.INSTANCE.glean_string_list_set(
this@StringListMetricType.handle,
ffiValueList,
value.size)
}
}
/**
* Sets a string list to one or more metric stores in a synchronous way.
*
* This is only to be used for the glean-ac to glean-core data migration.
*
* @param value This is a user defined string list.
*/
internal fun setSync(value: List<String>) {
if (disabled) {
return
}
val ffiValueList = StringArray(value.toTypedArray(), "utf-8")
LibGleanFFI.INSTANCE.glean_string_list_set(
this@StringListMetricType.handle,
ffiValueList,
value.size
)
}
/**
* Tests whether a value is stored for the metric for testing purposes only. This function will
* attempt to await the last task (if any) writing to the the metric's storage engine before
* returning a value.
*
* @param pingName represents the name of the ping to retrieve the metric for.
* Defaults to the first value in `sendInPings`.
* @return true if metric value exists, otherwise false
*/
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
@JvmOverloads
fun testHasValue(pingName: String = sendInPings.first()): Boolean {
@Suppress("EXPERIMENTAL_API_USAGE")
Dispatchers.API.assertInTestingMode()
val res = LibGleanFFI.INSTANCE.glean_string_list_test_has_value(this.handle, pingName)
return res.toBoolean()
}
/**
* Returns the stored value for testing purposes only. This function will attempt to await the
* last task (if any) writing to the the metric's storage engine before returning a value.
*
* @param pingName represents the name of the ping to retrieve the metric for.
* Defaults to the first value in `sendInPings`.
* @return value of the stored metric
* @throws [NullPointerException] if no value is stored
*/
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
@JvmOverloads
fun testGetValue(pingName: String = sendInPings.first()): List<String> {
@Suppress("EXPERIMENTAL_API_USAGE")
Dispatchers.API.assertInTestingMode()
if (!testHasValue(pingName)) {
throw NullPointerException("Metric has no value")
}
val jsonRes: JSONArray
val ptr = LibGleanFFI.INSTANCE.glean_string_list_test_get_value_as_json_string(
this.handle,
pingName)!!
try {
jsonRes = JSONArray(ptr.getAndConsumeRustString())
} catch (_: org.json.JSONException) {
throw NullPointerException("Could not parse metric as JSON")
}
return jsonRes.toList()
}
/**
* Returns the number of errors recorded for the given metric.
*
* @param errorType The type of the error recorded.
* @param pingName represents the name of the ping to retrieve the metric for.
* Defaults to the first value in `sendInPings`.
* @return the number of errors recorded for the metric.
*/
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
@JvmOverloads
fun testGetNumRecordedErrors(errorType: ErrorType, pingName: String = sendInPings.first()): Int {
@Suppress("EXPERIMENTAL_API_USAGE")
Dispatchers.API.assertInTestingMode()
return LibGleanFFI.INSTANCE.glean_string_list_test_get_num_recorded_errors(
this.handle, errorType.ordinal, pingName
)
}
}